#![warn(missing_docs)]
#![allow(clippy::missing_safety_doc)]
#![deny(unconditional_recursion)]
use generic_array::{GenericArray, typenum};
use crate::{
BranchfreeDivider, Divider,
divider::Denominator,
element::FloatElementWithBits,
isa::InstructionSet,
mask::{CastMask, GenericMask, GenericSelectable},
math::{FloatConsts, policy::Policy},
register::{Element, FloatElement, Lanes, NativeCapability},
};
mod num;
#[doc(hidden)]
pub mod splat;
#[allow(clippy::module_inception)]
mod vector;
pub mod ops;
pub mod streaming;
pub mod unaligned;
pub use self::num::NumVector;
pub use self::splat::{NewConst, NewVector, SplatConst, SplatVector, VectorValue, const_new, const_splat};
pub use self::vector::Vector;
pub use crate::register::StreamGroup;
pub trait MaskInteroperable<A, B>: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
where
A: GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
B: GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
{
}
impl<T, A, B> MaskInteroperable<A, B> for T
where
T: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>,
A: GenericVector<Lanes = T::Lanes, Mask: CastMask<T::Mask> + CastMask<B::Mask>>,
B: GenericVector<Lanes = T::Lanes, Mask: CastMask<T::Mask> + CastMask<A::Mask>>,
{
}
pub trait PartiallyInteroperable<A, B>:
GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
+ CastVector<Self>
+ CastVector<A>
+ CastVector<B>
where
A: CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
B: CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
{
}
impl<V, A, B> PartiallyInteroperable<A, B> for V
where
V: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
+ CastVector<V>
+ CastVector<A>
+ CastVector<B>,
A: CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<B::Mask>>,
B: CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<A::Mask>>,
{
}
pub trait FullyInteroperable<A, B>:
GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
+ BitCastVector<Self>
+ BitCastVector<A>
+ BitCastVector<B>
+ CastVector<Self>
+ CastVector<A>
+ CastVector<B>
where
A: BitCastVector<Self> + CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
B: BitCastVector<Self> + CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
{
}
impl<V, A, B> FullyInteroperable<A, B> for V
where
V: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
+ BitCastVector<V>
+ BitCastVector<A>
+ BitCastVector<B>
+ CastVector<V>
+ CastVector<A>
+ CastVector<B>,
A: BitCastVector<V> + CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<B::Mask>>,
B: BitCastVector<V> + CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<A::Mask>>,
{
}
trait GenericVectorExt: GenericVector {
#[inline(always)]
fn len_to_indices<I: UnsignedIntegerVector>(len: usize) -> I {
let Ok(len) = <<I as GenericVector>::Element as TryFrom<usize>>::try_from(len) else {
#[cfg(feature = "std")]
panic!("Length {} exceeds maximum supported index for this vector type", len);
#[cfg(not(feature = "std"))]
panic!("Length exceeds maximum supported index for this vector type");
};
I::splat(len)
}
}
impl<V: GenericVector> GenericVectorExt for V {}
pub trait VectorIndices<V: GenericVector>: UnsignedIntegerVector<Lanes = V::Lanes> {
unsafe fn gather_ptr(ptr: *const V::Element, indices: Self) -> V;
unsafe fn gather_ptr_m(src: V, mask: V::Mask, ptr: *const V::Element, indices: Self) -> V;
unsafe fn gather_ptr_z(mask: V::Mask, ptr: *const V::Element, indices: Self) -> V;
unsafe fn scatter_ptr(value: V, ptr: *mut V::Element, indices: Self);
unsafe fn scatter_ptr_m(value: V, mask: V::Mask, ptr: *mut V::Element, indices: Self);
}
pub trait IndexableVector<I: UnsignedIntegerVector<Lanes = Self::Lanes>>: GenericVector {
unsafe fn gather_ptr(ptr: *const Self::Element, indices: I) -> Self;
unsafe fn gather_ptr_m(src: Self, mask: Self::Mask, ptr: *const Self::Element, indices: I) -> Self;
unsafe fn gather_ptr_z(mask: Self::Mask, ptr: *const Self::Element, indices: I) -> Self;
unsafe fn scatter_ptr(value: Self, ptr: *mut Self::Element, indices: I);
unsafe fn scatter_ptr_m(value: Self, mask: Self::Mask, ptr: *mut Self::Element, indices: I);
}
impl<I, V> VectorIndices<V> for I
where
I: UnsignedIntegerVector<Lanes = V::Lanes>,
V: IndexableVector<I>,
{
#[inline(always)]
unsafe fn gather_ptr(ptr: *const <V as GenericVector>::Element, indices: Self) -> V {
unsafe { V::gather_ptr(ptr, indices) }
}
#[inline(always)]
unsafe fn gather_ptr_m(
src: V,
mask: <V as GenericVector>::Mask,
ptr: *const <V as GenericVector>::Element,
indices: Self,
) -> V {
unsafe { V::gather_ptr_m(src, mask, ptr, indices) }
}
#[inline(always)]
unsafe fn gather_ptr_z(
mask: <V as GenericVector>::Mask,
ptr: *const <V as GenericVector>::Element,
indices: Self,
) -> V {
unsafe { V::gather_ptr_z(mask, ptr, indices) }
}
#[inline(always)]
unsafe fn scatter_ptr(value: V, ptr: *mut <V as GenericVector>::Element, indices: Self) {
unsafe { V::scatter_ptr(value, ptr, indices) }
}
#[inline(always)]
unsafe fn scatter_ptr_m(
value: V,
mask: <V as GenericVector>::Mask,
ptr: *mut <V as GenericVector>::Element,
indices: Self,
) {
unsafe { V::scatter_ptr_m(value, mask, ptr, indices) }
}
}
pub trait Concat<HALF>: Extend<HALF> {
fn concat(lo: HALF, hi: HALF) -> Self;
fn split(self) -> (HALF, HALF);
}
pub trait Extend<FROM> {
fn extend(v: FROM) -> Self;
fn narrow(self) -> FROM;
}
pub trait ConcatVector<HALF: GenericVector<Element = Self::Element>>:
Concat<HALF> + GenericVector<Mask: Concat<HALF::Mask>>
{
}
pub trait ExtendVector<FROM: GenericVector<Element = Self::Element>>:
Extend<FROM> + GenericVector<Mask: Extend<FROM::Mask>>
{
}
impl<V: GenericVector, H: GenericVector<Element = V::Element>> ConcatVector<H> for V
where
V: Concat<H>,
V::Mask: Concat<H::Mask>,
{
}
impl<V: GenericVector, F: GenericVector<Element = V::Element>> ExtendVector<F> for V
where
V: Extend<F>,
V::Mask: Extend<F::Mask>,
{
}
pub trait SwizzleVector: GenericVector + crate::swizzle::Swizzle<Self::Lanes> {}
impl<V> SwizzleVector for V where V: GenericVector + crate::swizzle::Swizzle<V::Lanes> {}
pub trait Interleave: Sized {
fn interleave(self, other: Self) -> (Self, Self);
fn deinterleave(self, other: Self) -> (Self, Self);
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a Thermite vector type",
label = "not a SIMD vector",
note = "`GenericVector` is the root of Thermite's vector trait tower. It is implemented by `Vector<R>` (including the 1-lane scalar `Vector<f32>` / `Vector<f64>`) and by composite vector types such as `Dual`, `Complex`, and `Compensated`.",
note = "A bare scalar such as `f32` or `f64` is NOT a vector. Wrap it with `Vector::<f32>::splat(x)` (or `Vector(x)`) to get a 1-lane vector, or use the `ScalarMath` methods (`x.scalar_sin()`, ...) for one-off scalar math."
)]
pub trait GenericVector: 'static + Sized + Default + Copy + core::fmt::Debug
+ const_default::ConstDefault
+ SplatVector<Self::Element> + NewVector<Self::Element, Self::Lanes>
+ GenericSelectable<SelectableMask = Self::Mask>
+ crate::simd::HasIsa
+ CastVector<Self>
+ Interleave
{
type Element: Element;
const EMPTY: Self;
const LANES: usize;
#[inline(always)]
fn lanes() -> usize {
Self::LANES
}
type Lanes: Lanes;
type Unsigned: UnsignedIntegerVector<
Signed = Self::Signed,
Unsigned = Self::Unsigned,
Lanes = Self::Lanes,
Element = <Self::Element as Element>::Unsigned,
Mask: CastMask<Self::Mask> + CastMask<<Self::Signed as GenericVector>::Mask>,
> + CastVector<Self::Signed>
+ BitCastVector<Self::Signed>;
type Signed: SignedIntegerVector<
Signed = Self::Signed,
Unsigned = Self::Unsigned,
Lanes = Self::Lanes,
Element = <Self::Element as Element>::Signed,
Mask: CastMask<Self::Mask> + CastMask<<Self::Unsigned as GenericVector>::Mask>,
> + CastVector<Self::Unsigned>
+ BitCastVector<Self::Unsigned>;
type Mask: GenericMask
+ CastMask<<Self::Unsigned as GenericVector>::Mask>
+ CastMask<<Self::Signed as GenericVector>::Mask>;
fn new<const N: usize>(value: [Self::Element; N]) -> Self
where generic_array::typenum::Const<N>: generic_array::IntoArrayLength<ArrayLength = Self::Lanes>;
fn into_array(self) -> GenericArray<Self::Element, Self::Lanes>;
#[masked] fn splat(value: Self::Element) -> Self;
fn single(value: Self::Element) -> Self;
fn concat<INTO>(self, hi: Self) -> INTO
where
INTO: ConcatVector<Self, Element = Self::Element>,
{
<INTO as Concat<Self>>::concat(self, hi)
}
fn split<INTO: GenericVector>(self) -> (INTO, INTO)
where
Self: ConcatVector<INTO, Element = INTO::Element>,
{
<Self as Concat<INTO>>::split(self)
}
fn extend<INTO>(self) -> INTO
where
INTO: ExtendVector<Self, Element = Self::Element>,
{
<INTO as Extend<Self>>::extend(self)
}
fn narrow<INTO: GenericVector>(self) -> INTO
where
Self: ExtendVector<INTO, Element = INTO::Element>,
{
<Self as Extend<INTO>>::narrow(self)
}
#[inline(always)]
fn align_slice(slice: &[Self::Element]) -> (&[Self::Element], &[Self], &[Self::Element]) {
if const { size_of::<Self>() != (size_of::<Self::Element>() * Self::LANES) } {
return (slice, &[], &[]);
};
unsafe { slice.align_to() }
}
#[inline(always)]
fn align_slice_mut(slice: &mut [Self::Element]) -> (&mut [Self::Element], &mut [Self], &mut [Self::Element]) {
if const { size_of::<Self>() != (size_of::<Self::Element>() * Self::LANES) } {
return (slice, &mut [], &mut []);
};
unsafe { slice.align_to_mut() }
}
fn from_slice(slice: &[Self::Element]) -> Self {
assert!(slice.len() >= Self::lanes(), "Slice must have at least {} elements to create a vector", Self::lanes());
unsafe { Self::load_unaligned(slice.as_ptr()) }
}
fn copy_to_slice(self, slice: &mut [Self::Element]) {
assert!(slice.len() >= Self::lanes(), "Slice must have at least {} elements to copy from a vector", Self::lanes());
unsafe { self.store_unaligned(slice.as_mut_ptr()) }
}
fn iter_unaligned<'a>(values: &'a [Self::Element]) -> (unaligned::Unaligned<'a, Self>, &'a [Self::Element]) {
let num_vectors = values.len() / Self::lanes();
let offset = num_vectors * Self::lanes();
let head = &values[..offset];
let tail = &values[offset..];
(unaligned::Unaligned(head), tail)
}
fn iter_mut_unaligned<'a>(values: &'a mut [Self::Element]) -> (unaligned::UnalignedMut<'a, Self>, &'a mut [Self::Element]) {
let num_vectors = values.len() / Self::lanes();
let offset = num_vectors * Self::lanes();
let (head, tail) = values.split_at_mut(offset);
(unaligned::UnalignedMut(head), tail)
}
fn stream_aligned_slice<'a>(values: &'a [Self::Element]) -> impl DoubleEndedIterator<Item = streaming::StreamingVector<'a, Self>> {
let (&[], values, &[]) = Self::align_slice(values) else {
panic!("Slice is not aligned to the vector type, or has remaining elements");
};
values.iter().map(|v| streaming::StreamingVector(v))
}
fn stream_aligned_slice_mut<'a>(values: &'a mut [Self::Element]) -> impl DoubleEndedIterator<Item = streaming::StreamingVectorMut<'a, Self>> {
let (&mut [], values, &mut []) = Self::align_slice_mut(values) else {
panic!("Slice is not aligned to the vector type, or has remaining elements");
};
values.iter_mut().map(|v| streaming::StreamingVectorMut(v))
}
fn gather<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I) -> Self {
if indices.cmp_lt(Self::len_to_indices::<I>(slice.len())).all() {
unsafe { I::gather_ptr(slice.as_ptr(), indices) }
} else {
#[cfg(feature = "std")]
panic!("One or more indices are out of bounds for the slice length {}", slice.len());
#[cfg(not(feature = "std"))] panic!("One or more indices are out of bounds for the slice length");
}
}
fn gather_or<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I, or: Self) -> Self
where Self::Mask: CastMask<I::Mask>,
{
let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
unsafe { I::gather_ptr_m(or, in_bounds.cast(), slice.as_ptr(), indices) }
}
fn gather_or_zero<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I) -> Self
where Self::Mask: CastMask<I::Mask>,
{
let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
unsafe { I::gather_ptr_z(in_bounds.cast(), slice.as_ptr(), indices) }
}
fn gather_if<I: VectorIndices<Self>>(slice: &[Self::Element], enable: Self::Mask, indices: I, or: Self) -> Self
where
Self::Mask: CastMask<I::Mask>,
Self::Element: Default,
{
let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
unsafe { I::gather_ptr_m(or, enable & in_bounds.cast(), slice.as_ptr(), indices) }
}
fn scatter<I: VectorIndices<Self>>(self, slice: &mut [Self::Element], indices: I)
where Self::Mask: CastMask<I::Mask>,
{
let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
unsafe { I::scatter_ptr_m(self, in_bounds.cast(), slice.as_mut_ptr(), indices) }
}
fn scatter_if<I: VectorIndices<Self>>(self, slice: &mut [Self::Element], enable: Self::Mask, indices: I)
where Self::Mask: CastMask<I::Mask>,
{
let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
unsafe { I::scatter_ptr_m(self, enable & in_bounds.cast(), slice.as_mut_ptr(), indices) }
}
#[masked] unsafe fn load(ptr: *const Self::Element) -> Self;
unsafe fn load_unaligned(ptr: *const Self::Element) -> Self;
unsafe fn load_streaming(ptr: *const Self::Element) -> Self;
unsafe fn store(self, ptr: *mut Self::Element);
unsafe fn store_masked(self, mask: Self::Mask, ptr: *mut Self::Element);
unsafe fn store_unaligned(self, ptr: *mut Self::Element);
unsafe fn store_streaming(self, ptr: *mut Self::Element);
fn interleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self);
fn deinterleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self);
fn interleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N];
fn deinterleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N];
fn deinterleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N];
fn interleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N];
unsafe fn load_deinterleaved<const N: usize>(ptr: *const Self::Element) -> [Self; N];
unsafe fn store_interleaved<const N: usize>(ptr: *mut Self::Element, values: [Self; N]);
unsafe fn load_deinterleaved_arrays<const M: usize, const C: usize>(
ptr: *const Self::Element,
) -> [[Self; C]; M] {
const { assert!(M >= 1 && C >= 1) };
let mut out = [[Self::EMPTY; C]; M];
let mut j = 0;
while j < M {
let mut c = 0;
while c < C {
let mut v = Self::EMPTY;
let mut lane = 0;
while lane < Self::LANES {
v = v.insertv(lane, unsafe { ptr.add(lane * (M * C) + j * C + c).read_unaligned() });
lane += 1;
}
out[j][c] = v;
c += 1;
}
j += 1;
}
out
}
unsafe fn store_interleaved_arrays<const M: usize, const C: usize>(ptr: *mut Self::Element, values: [[Self; C]; M]) {
const { assert!(M >= 1 && C >= 1) };
let mut j = 0;
while j < M {
let mut c = 0;
while c < C {
let v = values[j][c];
let mut lane = 0;
while lane < Self::LANES {
unsafe { ptr.add(lane * (M * C) + j * C + c).write_unaligned(v.extractv(lane)) };
lane += 1;
}
c += 1;
}
j += 1;
}
}
unsafe fn load_deinterleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *const Self::Element,
) -> [StreamGroup<Self, TAIL>; M] {
const { assert!(M >= 1) };
let c = TAIL + 1;
let mut out = [StreamGroup { head: Self::EMPTY, tail: [Self::EMPTY; TAIL] }; M];
let mut j = 0;
while j < M {
let mut comp = 0;
while comp < c {
let mut v = Self::EMPTY;
let mut lane = 0;
while lane < Self::LANES {
v = v.insertv(lane, unsafe { ptr.add(lane * (M * c) + j * c + comp).read_unaligned() });
lane += 1;
}
if comp == 0 {
out[j].head = v;
} else {
out[j].tail[comp - 1] = v;
}
comp += 1;
}
j += 1;
}
out
}
unsafe fn store_interleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *mut Self::Element,
values: [StreamGroup<Self, TAIL>; M],
) {
const { assert!(M >= 1) };
let c = TAIL + 1;
let mut j = 0;
while j < M {
let mut comp = 0;
while comp < c {
let v = if comp == 0 { values[j].head } else { values[j].tail[comp - 1] };
let mut lane = 0;
while lane < Self::LANES {
unsafe { ptr.add(lane * (M * c) + j * c + comp).write_unaligned(v.extractv(lane)) };
lane += 1;
}
comp += 1;
}
j += 1;
}
}
fn lookup(values: &[Self::Element], indices: Self::Unsigned) -> Self {
let in_bounds = indices.cmp_lt(Self::len_to_indices::<Self::Unsigned>(values.len()));
unsafe { Self::lookup_unchecked(values, indices.zz(in_bounds)) }
}
unsafe fn lookup_unchecked(values: &[Self::Element], indices: Self::Unsigned) -> Self;
#[conditional] fn broadcast<const I: usize>(self) -> Self;
#[conditional] fn broadcastv(self, idx: usize) -> Self;
fn extract<const I: usize>(self) -> Self::Element;
#[inline(always)]
fn first(self) -> Self::Element {
self.extract::<0>()
}
fn extractv(self, idx: usize) -> Self::Element;
fn insert<const I: usize>(self, value: Self::Element) -> Self;
fn insertv(self, idx: usize, value: Self::Element) -> Self;
#[conditional] fn reverse(self) -> Self;
#[conditional] fn swap_bytes(self) -> Self;
fn zz(self, mask: Self::Mask) -> Self;
fn nz(self, mask: Self::Mask) -> Self;
#[inline(always)]
fn prefix_mask(n: usize) -> Self::Mask {
let n = if n > Self::lanes() { Self::lanes() } else { n };
let limit = Self::len_to_indices::<Self::Unsigned>(n);
Self::Unsigned::indexed().cmp_lt(limit).cast::<Self::Mask>()
}
#[inline(always)]
fn suffix_mask(n: usize) -> Self::Mask {
let n = if n > Self::lanes() { Self::lanes() } else { n };
let start = Self::len_to_indices::<Self::Unsigned>(Self::lanes() - n);
Self::Unsigned::indexed().cmp_ge(start).cast::<Self::Mask>()
}
fn compress(self, mask: Self::Mask) -> Self;
fn compress_z(self, mask: Self::Mask) -> Self;
fn compress_m(self, src: Self, mask: Self::Mask) -> Self;
fn expand(self, mask: Self::Mask) -> Self;
fn expand_z(self, mask: Self::Mask) -> Self;
fn expand_m(self, src: Self, mask: Self::Mask) -> Self;
fn align<const OFFSET: usize>(self, other: Self) -> Self;
const HAS_NATIVE_ALIGN: bool;
fn map<F>(self, f: F) -> Self
where
F: Fn(Self::Element) -> Self::Element;
fn fold<F>(self, init: Self::Element, f: F) -> Self::Element
where
F: Fn(Self::Element, Self::Element) -> Self::Element;
fn reduce<F>(self, f: F) -> Self::Element
where
F: Fn(Self::Element, Self::Element) -> Self::Element;
#[inline(always)] fn cast<INTO>(self) -> INTO
where
INTO: CastVector<Self>,
{
INTO::cast_from(self)
}
#[inline(always)] fn fast_cast<INTO>(self) -> INTO
where
INTO: CastVector<Self>,
{
INTO::fast_cast_from(self)
}
#[inline(always)] fn into_bits<INTO>(self) -> INTO
where
INTO: BitCastVector<Self>,
{
INTO::from_bits(self)
}
#[inline(always)] fn saturating_cast<INTO>(self) -> INTO
where
INTO: CastVector<Self>,
{
INTO::saturating_cast_from(self)
}
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` does not support bitwise vector operations",
label = "no `&`, `|`, `^`, `!`, andnot, or ternlog",
note = "`BitwiseVector` is implemented by integer and mask vectors. Floating-point vectors have no direct bitwise ops; reach their bits via `FloatVectorWithBits` (`.to_bits()` / reinterpret) first."
)]
pub trait BitwiseVector:
GenericVector
+ ops::BitAndMasked<Self::Mask, Self, Output = Self>
+ ops::BitAndAssignMasked<Self::Mask, Self>
+ ops::BitAndNotMasked<Self::Mask, Self, Output = Self>
+ ops::BitAndNotAssignMasked<Self::Mask, Self>
+ ops::BitOrMasked<Self::Mask, Self, Output = Self>
+ ops::BitOrAssignMasked<Self::Mask, Self>
+ ops::BitXorMasked<Self::Mask, Self, Output = Self>
+ ops::BitXorAssignMasked<Self::Mask, Self>
+ ops::NotMasked<Self::Mask, Output = Self>
{
#[conditional] fn ternlog<const IMM: i32>(a: Self, b: Self, c: Self) -> Self;
const HAS_NATIVE_TERNLOG: bool;
#[conditional] fn bilog<const IMM: i32>(a: Self, b: Self) -> Self;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` does not support bit-shift vector operations",
label = "no `<<`, `>>`, rotate, or byte-shift",
note = "`BitshiftVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float and mask vectors do not have shifts."
)]
pub trait BitshiftVector:
BitwiseVector
+ ops::ShrMasked<Self::Mask, Self::Unsigned, Output = Self>
+ ops::ShrAssignMasked<Self::Mask, Self::Unsigned>
+ ops::ShlMasked<Self::Mask, Self::Unsigned, Output = Self>
+ ops::ShlAssignMasked<Self::Mask, Self::Unsigned>
+ ops::ShrMasked<Self::Mask, u32, Output = Self>
+ ops::ShrAssignMasked<Self::Mask, u32>
+ ops::ShlMasked<Self::Mask, u32, Output = Self>
+ ops::ShlAssignMasked<Self::Mask, u32>
{
const HAS_TRUE_SHIFTV: bool;
const HAS_WIDE_BYTE_SHIFTS: bool;
#[conditional] fn bshli<const I: i32>(self) -> Self;
#[conditional] fn bshri<const I: i32>(self) -> Self;
#[conditional] fn shli<const I: i32>(self) -> Self;
#[conditional] fn shri<const I: i32>(self) -> Self;
#[conditional] fn shlv(self, counts: Self::Unsigned) -> Self;
#[conditional] fn shrv(self, counts: Self::Unsigned) -> Self;
#[conditional] fn rol(self, shift: u32) -> Self;
#[conditional] fn ror(self, shift: u32) -> Self;
#[conditional] fn roli<const I: i32>(self) -> Self;
#[conditional] fn rori<const I: i32>(self) -> Self;
#[conditional] fn rolv(self, counts: Self::Unsigned) -> Self;
#[conditional] fn rorv(self, counts: Self::Unsigned) -> Self;
#[conditional] fn reverse_bits(self) -> Self;
}
pub trait CastVector<FROM: Sized>: Sized {
fn cast_from(from: FROM) -> Self;
fn cast_into(self) -> FROM;
#[inline(always)]
fn saturating_cast_from(from: FROM) -> Self {
Self::cast_from(from)
}
#[inline(always)]
fn fast_cast_from(from: FROM) -> Self {
Self::cast_from(from)
}
#[inline(always)]
fn fast_cast_into(self) -> FROM {
Self::cast_into(self)
}
}
pub trait BitCastVector<FROM: Sized>: Sized {
fn from_bits(bits: FROM) -> Self;
}
pub trait PackedFloatVector<S: crate::element::float::spec::FloatSpec, F>: GenericVector {
fn pack(values: F) -> Self;
fn unpack(self) -> F;
}
pub trait Sad16Vector<W>: GenericVector {
fn sad16(self, other: Self) -> W;
}
pub trait Sad32Vector<W>: GenericVector {
fn sad32(self, other: Self) -> W;
fn sad32_accum(self, acc: W, other: Self) -> W;
}
pub trait Sad64Vector<W>: GenericVector {
fn sad64(self, other: Self) -> W;
fn sad64_accum(self, acc: W, other: Self) -> W;
}
#[derive(Debug, Clone, Copy)]
pub struct ValueGroups<V: PartialOrdVector> {
value: V,
remaining: V::Mask,
}
impl<V: PartialOrdVector> ValueGroups<V> {
#[inline(always)]
pub fn next_group(&mut self) -> Option<(V::Element, V::Mask)> {
let lane = self.remaining.first_set()?;
let group = self.remaining & self.value.cmp_eq(self.value.broadcastv(lane));
let value = self.value.extractv(lane);
self.remaining = crate::vector::ops::BitAndNot::bitandnot(self.remaining, group);
Some((value, group))
}
#[inline(always)]
pub fn remaining(&self) -> V::Mask {
self.remaining
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.remaining.none()
}
}
impl<V: PartialOrdVector> Iterator for ValueGroups<V> {
type Item = (V::Element, V::Mask);
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.next_group()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not support lane-wise comparisons",
label = "no `cmp_lt` / `cmp_le` / `cmp_gt` / `cmp_ge` / `cmp_eq` / `cmp_ne`",
note = "`PartialOrdVector` turns lane-wise comparisons into a `Mask`; it is implemented by all numeric vectors (integer and float)."
)]
pub trait PartialOrdVector: GenericVector + PartialEq {
#[inline(always)]
fn group_by_value(self, valid: Self::Mask) -> ValueGroups<Self> {
ValueGroups {
value: self,
remaining: valid,
}
}
fn cmp_lt(self, other: Self) -> Self::Mask;
fn cmp_le(self, other: Self) -> Self::Mask;
fn cmp_gt(self, other: Self) -> Self::Mask;
fn cmp_ge(self, other: Self) -> Self::Mask;
fn cmp_eq(self, other: Self) -> Self::Mask;
fn cmp_ne(self, other: Self) -> Self::Mask;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` does not support arithmetic vector operations",
label = "no `+`, `-`, `*`, `/`, `%`, min/max, or FMA",
note = "`NumericVector` is implemented by numeric vectors - integer (`Vector<i32>`, `i32xN`, ...) and float (`Vector<f32>`, `f32xN`, ...). Masks and bare scalars do not qualify.",
note = "A bare `f32`/`f64` is not a vector: wrap it in `Vector::<f32>::splat(x)` first."
)]
pub trait NumericVector:
PartialOrdVector<Element: num_traits::NumOps>
+ ops::AddMasked<Self::Mask, Self, Output = Self>
+ ops::AddAssignMasked<Self::Mask, Self>
+ ops::SubMasked<Self::Mask, Self, Output = Self>
+ ops::SubAssignMasked<Self::Mask, Self>
+ ops::MulMasked<Self::Mask, Self, Output = Self>
+ ops::MulAssignMasked<Self::Mask, Self>
+ ops::DivMasked<Self::Mask, Self, Output = Self>
+ ops::DivAssignMasked<Self::Mask, Self>
+ ops::RemMasked<Self::Mask, Self, Output = Self>
+ ops::RemAssignMasked<Self::Mask, Self>
+ ops::SquareMasked<Self::Mask, Output = Self>
+ num_traits::NumOps<Self>
+ num_traits::NumAssignOps<Self>
+ core::iter::Sum
+ core::iter::Product
{
const ZERO: Self;
const ONE: Self;
const TWO: Self;
const MIN: Self;
const MAX: Self;
fn to_signed_integer(self) -> Self::Signed;
fn from_signed_integer(v: Self::Signed) -> Self;
fn to_unsigned_integer(self) -> Self::Unsigned;
fn from_unsigned_integer(v: Self::Unsigned) -> Self;
#[inline(always)]
fn fast_to_signed_integer(self) -> Self::Signed {
self.to_signed_integer()
}
#[inline(always)]
fn fast_to_unsigned_integer(self) -> Self::Unsigned {
self.to_unsigned_integer()
}
fn is_zero(self) -> Self::Mask;
fn is_all_zero(self) -> bool;
#[conditional] fn min(self, other: Self) -> Self;
#[conditional] fn max(self, other: Self) -> Self;
fn sort_by<O: crate::sort::SortOrder>(self) -> Self;
fn bitonic_clean_by<O: crate::sort::SortOrder>(self) -> Self;
#[inline(always)]
fn sort(self) -> Self {
self.sort_by::<crate::sort::Ascending>()
}
#[inline(always)]
fn bitonic_clean(self) -> Self {
self.bitonic_clean_by::<crate::sort::Ascending>()
}
fn clamp(self, min: Self, max: Self) -> Self;
fn min_element(self) -> Self::Element;
fn max_element(self) -> Self::Element;
fn min_max_element(self) -> (Self::Element, Self::Element);
fn arg_minmax(self) -> (usize, usize);
#[conditional] fn scale(self, factor: Self::Element) -> Self;
fn pairwise_sum(lo: Self, hi: Self) -> Self;
fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self;
fn sum_elements(self) -> Self::Element;
fn prod_elements(self) -> Self::Element;
fn prefix_sum(self) -> Self;
fn prefix_min(self) -> Self;
fn prefix_max(self) -> Self;
fn reverse_prefix_sum(self) -> Self;
fn reverse_prefix_min(self) -> Self;
fn reverse_prefix_max(self) -> Self;
fn offset() -> Self;
fn indexed() -> Self;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a signed SIMD vector",
label = "no `abs`, `signum`, `copysign`, or unary `-`",
note = "`SignedVector` is implemented by signed integer and floating-point vectors. Unsigned integer vectors (`Vector<u32>`, `u8xN`, ...) are not signed."
)]
pub trait SignedVector: NumericVector + ops::NegMasked<Self::Mask, Output = Self> {
const NEG_ONE: Self;
const MIN_POSITIVE: Self;
#[conditional] fn abs(self) -> Self;
fn signum(self) -> Self;
#[conditional] fn copysign(self, sign: Self) -> Self;
fn is_positive(self) -> Self::Mask;
fn is_negative(self) -> Self::Mask;
fn select_negative(self, if_neg: Self, if_pos: Self) -> Self;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not an integer SIMD vector",
label = "not an integer vector",
note = "`IntegerVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float vectors implement `FloatVector` instead; convert with `.to_int()` or a cast."
)]
pub trait IntegerVector:
NumericVector<Element: Denominator>
+ BitshiftVector
+ ops::DivMasked<Self::Mask, Self::Divider, Output = Self>
+ ops::DivMasked<Self::Mask, Self::BranchfreeDivider, Output = Self>
+ num_traits::Saturating + num_traits::SaturatingAdd
+ num_traits::SaturatingSub + num_traits::WrappingMul
+ num_traits::WrappingAdd + num_traits::WrappingSub
{
type Divider: Copy;
type BranchfreeDivider: Copy;
type VectorizedDivider: Copy;
#[conditional] fn mulhi(self, other: Self) -> Self;
#[conditional] fn mullo(self, other: Self) -> Self;
#[conditional] fn saturating_add(self, other: Self) -> Self;
#[conditional] fn saturating_sub(self, other: Self) -> Self;
#[conditional] fn wrapping_sum(self) -> Self::Element;
#[conditional] fn wrapping_prod(self) -> Self::Element;
fn create_divider(d: Self::Element) -> Self::Divider;
fn create_branchfree_divider(d: Self::Element) -> Self::BranchfreeDivider;
fn to_divider(self) -> Self::VectorizedDivider;
#[conditional] fn count_ones(self) -> Self;
#[conditional] fn count_zeros(self) -> Self;
#[conditional] fn leading_ones(self) -> Self;
#[conditional] fn leading_zeros(self) -> Self;
#[conditional] fn trailing_ones(self) -> Self;
#[conditional] fn trailing_zeros(self) -> Self;
fn count_conflicts(self) -> Self;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a signed integer SIMD vector",
label = "not a signed integer vector",
note = "`SignedIntegerVector` is the meeting point of `SignedVector` and `IntegerVector`: it is implemented only by vectors of signed integer elements (`Vector<i32>`, `i16xN`, ...). Unsigned integer and float vectors do not qualify."
)]
pub trait SignedIntegerVector: SignedVector + IntegerVector<Element: crate::element::SignedIntegerElement> {
#[conditional] fn srai<const I: i32>(self) -> Self;
#[conditional] fn sra(self, count: u32) -> Self;
#[conditional] fn srav(self, counts: Self::Unsigned) -> Self;
#[conditional] fn avg_floor(self, other: Self) -> Self;
#[conditional] fn avg_ceil(self, other: Self) -> Self;
#[conditional] fn mulhrs(self, other: Self) -> Self;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not an unsigned integer SIMD vector",
label = "not an unsigned integer vector",
note = "`UnsignedIntegerVector` is implemented only by vectors of unsigned integer elements (`Vector<u32>`, `u8xN`, ...). Signed integer and float vectors do not qualify."
)]
pub trait UnsignedIntegerVector: IntegerVector<Element: crate::element::UnsignedIntegerElement> {
fn is_power_of_two(self) -> Self::Mask;
fn in_range(self, lo: Self, hi: Self) -> Self::Mask;
#[conditional] fn next_power_of_two_m1(self) -> Self;
#[conditional] fn ilog2p1(self) -> Self;
#[conditional] fn parity(self) -> Self;
#[conditional] fn avg(self, other: Self) -> Self;
#[conditional] fn abs_diff(self, other: Self) -> Self;
fn morton<const N: usize>(values: [Self; N]) -> Self;
fn reverse_morton<const N: usize>(self) -> [Self; N];
}
pub trait VectorWithRegister<R: crate::register::Register>: GenericVector {
fn into_register(self) -> crate::register::Storage<R>;
fn from_register(reg: crate::register::Storage<R>) -> Self;
fn as_slice(&self) -> &[Self::Element];
fn as_mut_slice(&mut self) -> &mut [Self::Element];
}
pub trait FloatVectorWithRegister:
FloatVectorWithBits<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
{
type Register: crate::register::FloatRegister<Element = Self::Element, Lanes = Self::Lanes>;
}
pub trait SignedIntegerVectorWithRegister:
SignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
{
type Register: crate::register::SignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
}
pub trait UnsignedIntegerVectorWithRegister:
UnsignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
{
type Register: crate::register::UnsignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
}
#[rustfmt::skip] #[thermite_macros::vector_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a floating-point SIMD vector",
label = "not a float vector",
note = "`FloatVector` is implemented by float vectors: the 1-lane `Vector<f32>` / `Vector<f64>`, the native-width `f32xN` / `f64xN`, and composite float types (`Dual`, `Complex`, `Compensated`).",
note = "Bare `f32` / `f64` do NOT implement `FloatVector`. Wrap the scalar first - `Vector::<f32>::splat(x)` or `Vector(x)` - or, for one-off scalar math, use `ScalarMath` (`x.scalar_sqrt()`, `x.scalar_exp()`, ...).",
note = "Integer vectors are not float vectors either; convert with `.to_float()` or a cast before calling float operations."
)]
pub trait FloatVector: SignedVector<Element: FloatElement>
+ FloatConsts
+ CastVector<Self::ExtendedPrecision>
+ ops::MulAddExtMasked<Self::Mask, Self, Self, Output = Self>
+ ops::MulAddAssignExtMasked<Self::Mask, Self, Self>
+ ops::AddSubExtMasked<Self::Mask, Output = Self>
{
const HALF: Self;
const NEG_ZERO: Self;
const INFINITY: Self;
const NEG_INFINITY: Self;
const NAN: Self;
const EPSILON: Self;
type ExtendedPrecision: FloatVector<Lanes = Self::Lanes> + CastVector<Self>;
fn is_infinite(self) -> Self::Mask;
fn is_finite(self) -> Self::Mask;
fn is_nan(self) -> Self::Mask;
fn is_zero_or_subnormal(self) -> Self::Mask;
fn is_normal(self) -> Self::Mask;
fn is_subnormal(self) -> Self::Mask;
const HAS_APPROX_RCP: bool;
const HAS_APPROX_RSQRT: bool;
#[conditional] fn sqrt(self) -> Self;
#[conditional] fn rsqrt(self) -> Self;
#[conditional] fn rcp(self) -> Self;
#[conditional] fn floor(self) -> Self;
#[conditional] fn ceil(self) -> Self;
#[conditional] fn round(self) -> Self;
#[conditional] fn trunc(self) -> Self;
#[conditional] fn fract(self) -> Self;
#[conditional] fn mul_sign(self, sign: Self) -> Self;
#[conditional] fn signed_zero(self) -> Self;
#[conditional] fn next_up(self) -> Self;
#[conditional] fn next_down(self) -> Self;
fn mix(self, a: Self, b: Self) -> Self;
#[inline(always)]
fn one_minus_sq(self) -> Self {
if const { Self::HAS_TRUE_FMA } {
self.nmul_add(self, Self::ONE)
} else {
(Self::ONE - self) * (Self::ONE + self)
}
}
unsafe fn block_autovectorization(&mut self);
fn with_bits<const N: usize, K: AsFloatVectorWithBitsKernel<Self, N>>(
_values: [Self; N],
_kernel: K,
) -> Option<<K as AsFloatVectorWithBitsKernel<Self, N>>::Output> {
None }
}
#[rustfmt::skip]
#[doc(hidden)]
#[macro_export]
macro_rules! scan_ladder {
(reverse, $v:expr, $fill:expr, $op:path) => {{
let mut v = $v;
let f = $fill;
let () = {
if const { Self::LANES > 1 } { v = $op(v, v.align::<1>(f)); }
if const { Self::LANES > 2 } { v = $op(v, v.align::<2>(f)); }
if const { Self::LANES > 4 } { v = $op(v, v.align::<4>(f)); }
if const { Self::LANES > 8 } { v = $op(v, v.align::<8>(f)); }
if const { Self::LANES > 16 } { v = $op(v, v.align::<16>(f)); }
if const { Self::LANES > 32 } { v = $op(v, v.align::<32>(f)); }
};
v
}};
(forward, $v:expr, $fill:expr, $op:path) => {{
let mut v = $v;
let f = $fill;
if const { Self::LANES.is_power_of_two() && Self::LANES <= 64 } {
let () = match const { Self::LANES } {
0 | 1 => {}
2 => { v = $op(v, f.align::<1>(v)); }
4 => { v = $op(v, f.align::<3>(v));
v = $op(v, f.align::<2>(v)); }
8 => { v = $op(v, f.align::<7>(v));
v = $op(v, f.align::<6>(v));
v = $op(v, f.align::<4>(v)); }
16 => { v = $op(v, f.align::<15>(v));
v = $op(v, f.align::<14>(v));
v = $op(v, f.align::<12>(v));
v = $op(v, f.align::<8>(v)); }
32 => { v = $op(v, f.align::<31>(v));
v = $op(v, f.align::<30>(v));
v = $op(v, f.align::<28>(v));
v = $op(v, f.align::<24>(v));
v = $op(v, f.align::<16>(v)); }
64 => { v = $op(v, f.align::<63>(v));
v = $op(v, f.align::<62>(v));
v = $op(v, f.align::<60>(v));
v = $op(v, f.align::<56>(v));
v = $op(v, f.align::<48>(v));
v = $op(v, f.align::<32>(v)); }
_ => unreachable!(),
};
v
} else {
$crate::scan_ladder!(reverse, v.reverse(), f, $op).reverse()
}
}};
}
#[macro_export]
macro_rules! with_bits {
([$($values:expr),+]: [$ty:ty; $len:literal] as fn($decl:ident: [$alias:ident; _]) -> $ret:ty $(where $($c:ty: $constraint:ident),*)? { $($body:tt)* }) => {{
struct AnonymousAsFloatVectorWithBitsKernel<V>(core::marker::PhantomData<V>);
impl<V: FloatVector> $crate::vector::AsFloatVectorWithBitsKernel<V, $len> for AnonymousAsFloatVectorWithBitsKernel<V>
$(where $($c: $constraint),*)?
{
type Output = $ret;
fn with_bits<
$alias: FloatVectorWithBits<
Element = V::Element,
Lanes = V::Lanes,
Mask = V::Mask,
Signed = V::Signed,
Unsigned = V::Unsigned,
ExtendedPrecision = V::ExtendedPrecision,
> + CastVector<V>,
>(
self,
$decl: [$alias; $len],
) -> Self::Output {
$($body)*
}
}
<V as FloatVector>::with_bits(
[$($values),+],
AnonymousAsFloatVectorWithBitsKernel::<V>(core::marker::PhantomData),
)
}};
}
pub trait AsFloatVectorWithBitsKernel<O: FloatVector, const N: usize> {
type Output;
fn with_bits<
V: FloatVectorWithBits<
Element = O::Element,
Lanes = O::Lanes,
Mask = O::Mask,
Signed = O::Signed,
Unsigned = O::Unsigned,
ExtendedPrecision = O::ExtendedPrecision,
> + CastVector<O>,
>(
self,
v: [V; N],
) -> Self::Output;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not expose float bit-manipulation operations",
label = "no `ldexp` / `frexp` or raw bit access",
note = "`FloatVectorWithBits` is implemented by concrete float vectors (`Vector<f32>`, `f32xN`, ...). Composite float types such as `Dual` / `Compensated` may not expose raw bit access, so bound on `FloatVector` instead unless you specifically need bit-level ops."
)]
pub trait FloatVectorWithBits:
BitwiseVector
+ FloatVector<Element: FloatElementWithBits, Signed: CastVector<Self::SignedBits>, Unsigned: CastVector<Self::Bits>>
+ GenericVector<
Signed: GenericVector<Mask: CastMask<<Self::SignedBits as GenericVector>::Mask>>,
Unsigned: GenericVector<Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>>,
> + FullyInteroperable<Self::Bits, Self::SignedBits>
{
type SignedBits: SignedIntegerVector<
Mask: CastMask<<Self::Signed as GenericVector>::Mask>,
Lanes = Self::Lanes,
Divider = Divider<<Self::Element as FloatElementWithBits>::SignedBits>,
BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::SignedBits>,
Element = <Self::Element as FloatElementWithBits>::SignedBits,
> + FullyInteroperable<Self, Self::Bits>
+ CastVector<Self::Signed>;
type Bits: UnsignedIntegerVector<
Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>,
Lanes = Self::Lanes,
Divider = Divider<<Self::Element as FloatElementWithBits>::Bits>,
BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::Bits>,
Element = <Self::Element as FloatElementWithBits>::Bits,
> + FullyInteroperable<Self, Self::SignedBits>
+ CastVector<Self::Unsigned>;
const NATIVE_CAP: NativeCapability;
unsafe fn native_ldexp(self, exp: Self::SignedBits) -> Self;
unsafe fn native_frexp(self) -> (Self, Self::SignedBits);
unsafe fn native_sin_cos<P: Policy>(self) -> (Self, Self);
unsafe fn native_sin<P: Policy>(self) -> Self;
unsafe fn native_cos<P: Policy>(self) -> Self;
unsafe fn native_tan<P: Policy>(self) -> Self;
unsafe fn native_exp2<P: Policy>(self) -> Self;
unsafe fn native_log2<P: Policy>(self) -> Self;
unsafe fn native_exp<P: Policy>(self) -> Self;
unsafe fn native_ln<P: Policy>(self) -> Self;
unsafe fn native_powf<P: Policy>(self, exp: Self) -> Self;
fn total_order(self) -> Self::SignedBits;
fn linear_order(self) -> Self::SignedBits;
}
#[rustfmt::skip]
pub trait GenericVector2: GenericVector {
#[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
#[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
}
#[rustfmt::skip]
pub trait GenericVector3: GenericVector {
#[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
#[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
#[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
}
#[rustfmt::skip]
pub trait GenericVector4: GenericVector {
#[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
#[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
#[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
#[inline(always)] fn w(&self) -> Self::Element { self.extract::<3>() }
}
impl<V: GenericVector<Lanes = typenum::U2>> GenericVector2 for V {}
impl<V: GenericVector<Lanes = typenum::U3>> GenericVector3 for V {}
impl<V: GenericVector<Lanes = typenum::U4>> GenericVector4 for V {}
#[rustfmt::skip]
macro_rules! impl_swizzle4 {
(@ x) => { 0 };
(@ y) => { 1 };
(@ z) => { 2 };
(@ w) => { 3 };
(IMPL x x x x) => { #[inline(always)] fn xxxx(self) -> Self { self.broadcast::<0>() } };
(IMPL y y y y) => { #[inline(always)] fn yyyy(self) -> Self { self.broadcast::<1>() } };
(IMPL z z z z) => { #[inline(always)] fn zzzz(self) -> Self { self.broadcast::<2>() } };
(IMPL w w w w) => { #[inline(always)] fn wwww(self) -> Self { self.broadcast::<3>() } };
(IMPL $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
#[inline(always)]
fn [<$a $b $c $d>](self) -> Self {
struct Indices;
impl crate::swizzle::SwizzleIndices<typenum::U4> for Indices {
const INDICES: GenericArray<u32, typenum::U4> = {
unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U4>>([
impl_swizzle4!(@ $a),
impl_swizzle4!(@ $b),
impl_swizzle4!(@ $c),
impl_swizzle4!(@ $d)
]) }
};
}
self.permute_const::<Indices>()
}
}};
(DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
#[allow(missing_docs)]
$(#[$meta])* fn [<$a $b $c $d>](self) -> Self;
}};
($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident $d:ident]),*) => {
pub trait Swizzle4: SwizzleVector<Lanes = typenum::U4> { $(impl_swizzle4!(DECL $(#[$meta])* $a $b $c $d);)* }
impl<V: SwizzleVector<Lanes = typenum::U4>> Swizzle4 for V {
$(impl_swizzle4!(IMPL $a $b $c $d);)*
}
}
}
#[rustfmt::skip]
macro_rules! impl_swizzle3 {
(IMPL x x x) => { #[inline(always)] fn xxx(self) -> Self { self.broadcast::<0>() } };
(IMPL y y y) => { #[inline(always)] fn yyy(self) -> Self { self.broadcast::<1>() } };
(IMPL z z z) => { #[inline(always)] fn zzz(self) -> Self { self.broadcast::<2>() } };
(IMPL $a:ident $b:ident $c:ident) => {paste::paste! {
#[inline(always)]
fn [<$a $b $c>](self) -> Self {
struct Indices;
impl crate::swizzle::SwizzleIndices<typenum::U3> for Indices {
const INDICES: GenericArray<u32, typenum::U3> = {
unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U3>>([
impl_swizzle4!(@ $a),
impl_swizzle4!(@ $b),
impl_swizzle4!(@ $c)
]) }
};
}
self.permute_const::<Indices>()
}
}};
(DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident) => {paste::paste! {
#[allow(missing_docs)]
$(#[$meta])* fn [<$a $b $c>](self) -> Self;
}};
($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident]),*) => {
pub trait Swizzle3: SwizzleVector<Lanes = typenum::U3> { $(impl_swizzle3!(DECL $(#[$meta])* $a $b $c);)* }
impl<V: SwizzleVector<Lanes = typenum::U3>> Swizzle3 for V {
$(impl_swizzle3!(IMPL $a $b $c);)*
}
}
}
impl_swizzle3! {
[x y z], [x x x], [x x y], [x x z], [x y x], [x y y], [x z x], [x z y], [x z z],
[y x x], [y x y], [y x z], [y y x], [y y y], [y y z], [y z x], [y z y], [y z z],
[z x x], [z x y], [z x z], [z y x], [z y y], [z y z], [z z x], [z z y], [z z z]
}
impl_swizzle4! {
[x y z w], [x x x x], [x x x y], [x x x z], [x x x w], [x x y x], [x x y y], [x x y z],
[x x y w], [x x z x], [x x z y], [x x z z], [x x z w], [x x w x], [x x w y], [x x w z],
[x x w w], [x y x x], [x y x y], [x y x z], [x y x w], [x y y x], [x y y y], [x y y z],
[x y y w], [x y z x], [x y z y], [x y z z], [x y w x], [x y w y], [x y w z], [x y w w],
[x z x x], [x z x y], [x z x z], [x z x w], [x z y x], [x z y y], [x z y z], [x z y w],
[x z z x], [x z z y], [x z z z], [x z z w], [x z w x], [x z w y], [x z w z], [x z w w],
[x w x x], [x w x y], [x w x z], [x w x w], [x w y x], [x w y y], [x w y z], [x w y w],
[x w z x], [x w z y], [x w z z], [x w z w], [x w w x], [x w w y], [x w w z], [x w w w],
[y x x x], [y x x y], [y x x z], [y x x w], [y x y x], [y x y y], [y x y z], [y x y w],
[y x z x], [y x z y], [y x z z], [y x z w], [y x w x], [y x w y], [y x w z], [y x w w],
[y y x x], [y y x y], [y y x z], [y y x w], [y y y x], [y y y y], [y y y z], [y y y w],
[y y z x], [y y z y], [y y z z], [y y z w], [y y w x], [y y w y], [y y w z], [y y w w],
[y z x x], [y z x y], [y z x z], [y z x w], [y z y x], [y z y y], [y z y z], [y z y w],
[y z z x], [y z z y], [y z z z], [y z z w], [y z w x], [y z w y], [y z w z], [y z w w],
[y w x x], [y w x y], [y w x z], [y w x w], [y w y x], [y w y y], [y w y z], [y w y w],
[y w z x], [y w z y], [y w z z], [y w z w], [y w w x], [y w w y], [y w w z], [y w w w],
[z x x x], [z x x y], [z x x z], [z x x w], [z x y x], [z x y y], [z x y z], [z x y w],
[z x z x], [z x z y], [z x z z], [z x z w], [z x w x], [z x w y], [z x w z], [z x w w],
[z y x x], [z y x y], [z y x z], [z y x w], [z y y x], [z y y y], [z y y z], [z y y w],
[z y z x], [z y z y], [z y z z], [z y z w], [z y w x], [z y w y], [z y w z], [z y w w],
[z z x x], [z z x y], [z z x z], [z z x w], [z z y x], [z z y y], [z z y z], [z z y w],
[z z z x], [z z z y], [z z z z], [z z z w], [z z w x], [z z w y], [z z w z], [z z w w],
[z w x x], [z w x y], [z w x z], [z w x w], [z w y x], [z w y y], [z w y z], [z w y w],
[z w z x], [z w z y], [z w z z], [z w z w], [z w w x], [z w w y], [z w w z], [z w w w],
[w x x x], [w x x y], [w x x z], [w x x w], [w x y x], [w x y y], [w x y z], [w x y w],
[w x z x], [w x z y], [w x z z], [w x z w], [w x w x], [w x w y], [w x w z], [w x w w],
[w y x x], [w y x y], [w y x z], [w y x w], [w y y x], [w y y y], [w y y z], [w y y w],
[w y z x], [w y z y], [w y z z], [w y z w], [w y w x], [w y w y], [w y w z], [w y w w],
[w z x x], [w z x y], [w z x z], [w z x w], [w z y x], [w z y y], [w z y z], [w z y w],
[w z z x], [w z z y], [w z z z], [w z z w], [w z w x], [w z w y], [w z w z], [w z w w],
[w w x x], [w w x y], [w w x z], [w w x w], [w w y x], [w w y y], [w w y z], [w w y w],
[w w z x], [w w z y], [w w z z], [w w z w], [w w w x], [w w w y], [w w w z], [w w w w]
}
pub trait LinAlg3Vector: FloatVector {
fn dot3(self, other: Self) -> Self::Element;
fn cross3<const DOP: bool>(self, other: Self) -> Self;
fn refract(self, n: Self, eta: Self::Element) -> Self;
fn zero4(self) -> Self;
fn one4(self) -> Self;
fn min_element3(self) -> Self::Element;
fn max_element3(self) -> Self::Element;
fn sum_elements3(self) -> Self::Element;
fn prod_elements3(self) -> Self::Element;
fn mat3_transpose(m: &[Self; 3]) -> [Self; 3];
fn mat3_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 3]) -> Self;
fn mat3_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
m: &[Self; 3],
vectors: &[Self; N],
) -> [Self; N];
fn mat3_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 3], rhs: &[Self; 3]) -> [Self; 3];
fn mat3_det(m: &[Self; 3]) -> Self::Element;
fn mat3_inverse_inplace(m: &mut [Self; 3]) -> Self::Element;
#[inline(always)]
fn mat3_inverse(m: &[Self; 3]) -> Option<[Self; 3]> {
let mut mat = *m;
if Self::mat3_inverse_inplace(&mut mat) == Self::Element::ZERO {
None
} else {
Some(mat)
}
}
fn mat3_normal<const DIVIDE: bool>(m: &[Self; 3]) -> [Self; 3];
}
pub trait LinAlg4Vector: LinAlg3Vector {
fn dot4(self, other: Self) -> Self::Element;
fn quat4_product(self, other: Self) -> Self;
fn quat4_vec3_product<const DOP: bool>(self, vec: Self) -> Self;
fn quat_to_mat3<const COLUMN_MAJOR: bool>(self) -> [Self; 3];
fn quat_to_mat4<const COLUMN_MAJOR: bool>(self) -> [Self; 4];
fn mat4_transpose(m: &[Self; 4]) -> [Self; 4];
fn mat4_vec4_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
fn mat4_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
fn mat4_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
m: &[Self; 4],
vectors: &[Self; N],
) -> [Self; N];
fn mat4_point3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
fn mat4_point3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
m: &[Self; 4],
points: &[Self; N],
) -> [Self; N];
fn mat4_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 4], rhs: &[Self; 4]) -> [Self; 4];
fn mat4_vec4_product_array<const COLUMN_MAJOR: bool, const N: usize>(
m: &[Self; 4],
vectors: &[Self; N],
) -> [Self; N];
fn mat4_inverse_inplace(m: &mut [Self; 4]) -> Self::Element;
fn mat4_det(m: &[Self; 4]) -> Self::Element;
#[inline(always)]
fn mat4_inverse(m: &[Self; 4]) -> Option<[Self; 4]> {
let mut mat = *m;
if crate::likely(Self::mat4_inverse_inplace(&mut mat) != Self::Element::ZERO) {
Some(mat)
} else {
None
}
}
}