viffy 0.1.5

SoA + SIMD automata generator
Documentation
use std::ops::AddAssign;
use std::ops::SubAssign;
use std::ops::MulAssign;
use std::ops::DivAssign;
use std::ops::RemAssign;
use std::ops::BitAndAssign;
use std::ops::BitOrAssign;
use std::ops::BitXorAssign;
use std::ops::Shr;
use std::ops::ShrAssign;
use std::ops::Shl;
use std::ops::ShlAssign;
use std::ops::Index;
use std::ops::IndexMut;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ops::{Add, Sub, Mul, Div, Rem, BitOr, BitXor, BitAnd, Not, Neg};
use std::slice::SliceIndex;
use crate::NumericTrait;
use crate::SerialNumericTrait;

use super::LossyFrom;

/// Default vector size
pub const VECTOR_SIZE: usize = 8;

#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq, Hash)]
pub struct NumericVector<T, const SEQ: bool>([T; VECTOR_SIZE]);
impl<T: Copy, const SEQ: bool> From<T> for NumericVector<T, SEQ> {
	fn from(value: T) -> Self {
		Self([value; VECTOR_SIZE])
	}
}
impl<T: Copy, const SEQ: bool> NumericVector<T, SEQ> {
	pub fn get(self, index: usize) -> T {
		unsafe { std::hint::assert_unchecked(index < VECTOR_SIZE) }
		self.0[index]
	}
	pub fn set(self, value: T, index: usize) -> Self {
		unsafe { std::hint::assert_unchecked(index < VECTOR_SIZE) }
		let mut s = self;
		s.0[index] = value;
		s
	}
}
macro_rules! basic_ops {
	($(impl $trait:ident $call:ident $assign_trait:ident $call_assign:ident)*) => {
		$(
			impl<T: $trait<Output = T> + Copy, const SEQ: bool> $trait for NumericVector<T, SEQ> {
				type Output = NumericVector<T, SEQ>;
				fn $call(self, rhs: Self) -> Self {
					Self(core::array::from_fn(|i| self.0[i].$call(rhs.0[i])))
				}
			}
			impl<T: $assign_trait + Copy, const SEQ: bool> $assign_trait for NumericVector<T, SEQ> {
				fn $call_assign(&mut self, rhs: Self) {
					for i in 0..VECTOR_SIZE {
						self.0[i].$call_assign(rhs.0[i]);
					}
				}
			}
		)*
	};
}
basic_ops! {
	impl Add add AddAssign add_assign
	impl Sub sub SubAssign sub_assign
	impl Mul mul MulAssign mul_assign
	impl Div div DivAssign div_assign
	impl Rem rem RemAssign rem_assign
	impl BitOr bitor BitOrAssign bitor_assign
	impl BitAnd bitand BitAndAssign bitand_assign
	impl BitXor bitxor BitXorAssign bitxor_assign
	impl Shl shl ShlAssign shl_assign
	impl Shr shr ShrAssign shr_assign
}

impl<T: Not<Output = T> + Copy, const SEQ: bool> Not for NumericVector<T, SEQ> {
	type Output = NumericVector<T, SEQ>;
	fn not(self) -> Self::Output {
		Self(core::array::from_fn(|i| self.0[i].not()))
	}
}
impl<T: Neg<Output = T> + Copy, const SEQ: bool> Neg for NumericVector<T, SEQ> {
	type Output = NumericVector<T, SEQ>;
	fn neg(self) -> Self::Output {
		Self(core::array::from_fn(|i| self.0[i].neg()))
	}
}
impl<T, const SEQ: bool> AsMut<[T]> for NumericVector<T, SEQ> {
	fn as_mut(&mut self) -> &mut [T] {
		&mut self.0[..]
	}
}
impl<T, const SEQ: bool> AsRef<[T]> for NumericVector<T, SEQ> {
	fn as_ref(&self) -> &[T] {
		&self.0[..]
	}
}
impl<T, const SEQ: bool> Deref for NumericVector<T, SEQ> {
	type Target = [T];
	fn deref(&self) -> &[T] {
		&self.0[..]
	}
}
impl<T, const SEQ: bool> DerefMut for NumericVector<T, SEQ> {
	fn deref_mut(&mut self) -> &mut [T] {
		&mut self.0[..]
	}
}
impl<T, I: SliceIndex<[T]>, const SEQ: bool> Index<I> for NumericVector<T, SEQ> {
	type Output = I::Output;
	fn index(&self, index: I) -> &Self::Output {
		Index::index(&self.0, index)
	}
}
impl<T, I: SliceIndex<[T]>, const SEQ: bool> IndexMut<I> for NumericVector<T, SEQ> {
	fn index_mut(&mut self, index: I) -> &mut I::Output {
		IndexMut::index_mut(&mut self.0, index)
	}
}
//
pub type FloatVector = NumericVector<f32, false>;
pub type IntegerVector = NumericVector<i32, false>;
pub type ContigousIntegerVector = NumericVector<i32, true>;

#[macro_export]
macro_rules! impl_vectorizable_trait {
	($x:ty, $y:ty) => {
		impl VectorizableTrait<$y> for $x {
			fn into_vector(self) -> $y { <$y>::from(self as i32) }
			fn from_vector(v: &$y, index: usize) -> Self { v.get(index) as Self }
		}
	};
}
impl_vectorizable_trait!(u8, IntegerVector);
impl_vectorizable_trait!(u16, IntegerVector);
impl_vectorizable_trait!(u32, IntegerVector);
impl_vectorizable_trait!(u64, IntegerVector);
impl_vectorizable_trait!(i8, IntegerVector);
impl_vectorizable_trait!(i16, IntegerVector);
impl_vectorizable_trait!(i32, IntegerVector);
impl_vectorizable_trait!(i64, IntegerVector);
impl VectorizableTrait<FloatVector> for f32 {
	fn into_vector(self) -> FloatVector { FloatVector::from(self) }
	fn from_vector(v: &FloatVector, index: usize) -> Self { v.get(index) }
}

// Trait to transform serial patterns into vector patterns
pub trait VectorizableTrait<T> {
	fn into_vector(self) -> T;
	fn from_vector(v: &T, index: usize) -> Self;
}

pub trait ParameterSerialTrait
where
	Self: SerialNumericTrait + LossyFrom<u16> + PartialEq
{

}
impl ParameterSerialTrait for u8 {}
impl ParameterSerialTrait for u16 {}
impl ParameterSerialTrait for u32 {}
impl ParameterSerialTrait for u64 {}
impl ParameterSerialTrait for i8 {}
impl ParameterSerialTrait for i16 {}
impl ParameterSerialTrait for i32 {}
impl ParameterSerialTrait for i64 {}
impl ParameterSerialTrait for f32 {}
impl ParameterSerialTrait for f64 {}

/// Trait to perform vector patterns using "serial" functions
/// note that this trait is also implemented for non-vector types
pub trait ParameterVectorTrait
where
	Self: Sized + NumericTrait + From<Self::SerialType>,
	Self::SerialType: ParameterSerialTrait,
{
	type SerialType = i32;
	/// Composes a vector by giving an index
	#[inline]
	fn compose<F: FnMut(usize) -> Self::SerialType>(mut self, mut f: F) -> Self {
		for index in 0..VECTOR_SIZE {
			self = self.insert(f(index), index);
		}
		self
	}
	/// Composes a vector, but hands out the old values instead of an index
	#[inline]
	fn compose_with<F: FnMut(Self::SerialType) -> Self::SerialType>(mut self, mut f: F) -> Self {
		for index in 0..VECTOR_SIZE {
			self = self.insert(f(self.extract(index)), index);
		}
		self
	}
	/// Composes a new vector via a mapping
	#[inline]
	fn compose_map<T, F>(self, mut f: F) -> T
	where
		F: FnMut(Self::SerialType) -> T::SerialType,
		T: ParameterVectorTrait + Default
	{
		let mut v = T::default();
		for index in 0..VECTOR_SIZE {
			v = v.insert(f(self.extract(index)), index);
		}
		v
	}
	#[inline]
	fn decompose<F: FnMut(usize)>(self, mut f: F) {
		for index in 0..VECTOR_SIZE {
			f(index);
		}
	}
	#[inline]
	fn decompose_with<F: FnMut(Self::SerialType)>(self, mut f: F) {
		for index in 0..VECTOR_SIZE {
			f(self.extract(index));
		}
	}
	fn extract(self, index: usize) -> Self::SerialType;
	fn insert(self, value: Self::SerialType, index: usize) -> Self;
	fn seq(v: Self::SerialType) -> Self::SerialType { v }
}
impl ParameterVectorTrait for IntegerVector where Self: NumericTrait {
	type SerialType = i32;
	#[inline]
	fn extract(self, index: usize) -> i32 { self.get(index) }
	#[inline]
	fn insert(self, value: i32, index: usize) -> Self { self.set(value, index) }
	#[inline]
	fn seq(v: Self::SerialType) -> Self::SerialType {
		v + 1
	}
}
impl ParameterVectorTrait for ContigousIntegerVector where Self: NumericTrait {
	type SerialType = i32;
	/// Composes a vector by giving an index
	#[inline]
	fn compose<F: FnMut(usize) -> Self::SerialType>(self, _: F) -> Self {
		unsafe { std::hint::unreachable_unchecked() }
	}
	/// Composes a vector, but hands out the old values instead of an index
	#[inline]
	fn compose_with<F: FnMut(Self::SerialType) -> Self::SerialType>(self, _: F) -> Self {
		unsafe { std::hint::unreachable_unchecked() }
	}
	/// Composes a new vector via a mapping
	#[inline]
	fn compose_map<T, F>(self, _: F) -> T
	where
		F: FnMut(Self::SerialType) -> T::SerialType,
		T: ParameterVectorTrait
	{
		unsafe { std::hint::unreachable_unchecked() }
	}
	/// Property that xn = n, x1 = n + 1, and x2 = n2, shall hold for continous
	/// integer vectors, otherwise it's undefined behaviour
	#[inline]
	fn extract(self, index: usize) -> i32 {
		let base = self[0] + index as i32;
		unsafe { std::hint::assert_unchecked(self[index] == base); }
		base
	}
	#[inline]
	fn insert(self, value: i32, index: usize) -> Self { self.set(value, index) }
	#[inline]
	fn seq(v: Self::SerialType) -> Self::SerialType {
		v + 1
	}
}
impl ParameterVectorTrait for FloatVector where Self: NumericTrait {
	type SerialType = f32;
	#[inline]
	fn extract(self, index: usize) -> f32 { self[index] }
	#[inline]
	fn insert(self, value: f32, index: usize) -> Self { self.set(value, index) }
}
impl<T: ParameterSerialTrait + From<u16> + Into<i32> + Default + Index<usize>> ParameterVectorTrait for T where Self: NumericTrait {
	type SerialType = T;
	#[inline]
	fn extract(self, _: usize) -> T { self }
	#[inline]
	fn insert(self, value: T, _: usize) -> Self { value }
}

/// Trait implemented for return types of vectorized functions
pub trait ResultVectorTrait
where
	Self: ParameterVectorTrait
	+ Not<Output = Self>
	+ BitOr<Output = Self>
	+ BitAnd<Output = Self>
{
	/// Prepares a vector to be used as a mask, broadcasts the LSB
	/// of the vector unto itself
	fn to_mask_vector(self) -> Self;
	fn from_bool(cond: bool) -> Self;
	fn from_bool_to_serial(cond: bool) -> Self::SerialType;
}
impl ResultVectorTrait for NumericVector<i32, false> {
	fn to_mask_vector(self) -> Self { self }
	fn from_bool(cond: bool) -> Self { Self::from(cond as i32) }
	fn from_bool_to_serial(cond: bool) -> Self::SerialType {
		Self::SerialType::from(cond as i32)
	}
}

pub trait ComposeTupleUnpackerTrait<T, U> {
	fn lambda<F: Fn(T) -> U>(&self, _: F, _: usize) -> U;
}
macro_rules! impl_compose_tuple_extract {
	($s:tt, $index:ident $($element:ident)*) => {
		{
			let ($($element,)*) = $s;
			($($element.extract($index),)*)
		}
	};
}
macro_rules! impl_compose_tuple_unpacker {
	() => {};
	($head:ident $($tail:ident)*) => {
		impl<O: ParameterSerialTrait, $head: ParameterVectorTrait + Copy, $($tail: ParameterVectorTrait + Copy),*> ComposeTupleUnpackerTrait<($head::SerialType, $($tail::SerialType),*), O> for ($head, $($tail),*) {
			fn lambda<F: Fn(($head::SerialType, $($tail::SerialType),*)) -> O>(&self, f: F, index: usize) -> O {
				f(impl_compose_tuple_extract!(self, index $head $($tail)*))
			}
		}
		impl_compose_tuple_unpacker!($($tail)*);
	};
}
impl_compose_tuple_unpacker!(A B C D E);
pub trait ComposeTupleParameterTrait {}
macro_rules! impl_compose_tuple_generic {
	() => {};
	($head:ident $($tail:ident)*) => {
		impl<$head, $($tail),*> ComposeTupleParameterTrait for ($head, $($tail),*) {}
		impl_compose_tuple_generic!($($tail)*);
	};
}
impl_compose_tuple_generic!(A B C D E);

/// Use this to quickly compose a vector from serializer-producer closure
#[inline]
pub fn compose_vector<O: ResultVectorTrait + Default, A: ComposeTupleUnpackerTrait<U, O::SerialType>, U: ComposeTupleParameterTrait, F: Fn(U) -> O::SerialType + Copy>(tags: A, lambda: F) -> O {
	O::default().compose(|index| tags.lambda(lambda, index))
}