vecstasy 0.1.10

vecstasy is a library for SIMD-enabled floating-point operations on vectors.
Documentation
use crate::VecLike;
use std::simd::Simd;
use std::simd::num::SimdFloat;

pub const SIMD_LANECOUNT: usize = 8;
pub type SimdUnit = Simd<f32, SIMD_LANECOUNT>;

impl VecLike for [f32] {
    type Owned = Vec<f32>;

    /// Computes the squared L2 (Euclidean) distance between `self` and `othr`.
    ///
    /// Operates on fixed‐size chunks; any trailing elements when the slice length
    /// is not a multiple of the chunk size will be silently ignored in release mode.
    ///
    /// # Panics
    /// - In debug mode, if `self.len() != othr.len()`.
    /// - In debug mode, if the slice length is not a multiple of the internal chunk size.
    #[inline]
    fn l2_dist_squared(&self, othr: &Self) -> f32 {
        debug_assert!(self.len() == othr.len());
        debug_assert!(self.len() % SIMD_LANECOUNT == 0);

        let mut intermediate_sum_x8 = SimdUnit::splat(0.0);

        let self_chunks = self.chunks_exact(SIMD_LANECOUNT);
        let othr_chunks = othr.chunks_exact(SIMD_LANECOUNT);

        for (slice_self, slice_othr) in self_chunks.zip(othr_chunks) {
            let f32x8_slf = SimdUnit::from_slice(slice_self);
            let f32x8_oth = SimdUnit::from_slice(slice_othr);
            let diff = f32x8_slf - f32x8_oth;
            intermediate_sum_x8 += diff * diff;
        }

        intermediate_sum_x8.reduce_sum() // 8-to-1 sum
    }

    /// Computes the dot product of `self` and `othr`.
    ///
    /// Operates on fixed‐size chunks; any trailing elements when the slice length
    /// is not a multiple of the chunk size will be silently ignored in release mode.
    ///
    /// # Panics
    /// - In debug mode, if `self.len() != othr.len()`.
    /// - In debug mode, if the slice length is not a multiple of the internal chunk size.
    #[inline]
    fn dot(&self, othr: &Self) -> f32 {
        debug_assert!(self.len() == othr.len());
        debug_assert!(self.len() % SIMD_LANECOUNT == 0);

        // accumulator vector of zeroes
        let mut accumulated = SimdUnit::splat(0.0);

        let self_chunks = self.chunks_exact(SIMD_LANECOUNT);
        let othr_chunks = othr.chunks_exact(SIMD_LANECOUNT);

        for (slice_self, slice_othr) in self_chunks.zip(othr_chunks) {
            // load each chunk into a SIMD register
            let vx = SimdUnit::from_slice(slice_self);
            let vy = SimdUnit::from_slice(slice_othr);
            // multiply-and-accumulate
            accumulated += vx * vy;
        }

        // horizontal sum across lanes
        accumulated.reduce_sum()
    }

    /// Returns a normalized copy of the input slice.
    ///
    /// Operates on fixed‐size chunks; any trailing elements when the slice length
    /// is not a multiple of the chunk size will be silently ignored in release mode.
    ///
    /// If the input norm is zero, returns a zero vector of the same length.
    ///
    /// # Panics
    /// - In debug mode, if the slice length is not a multiple of the internal chunk size.
    #[inline]
    fn normalized(&self) -> Self::Owned {
        let norm = self.dot(self).sqrt();
        if norm == 0.0 {
            // avoid division by zero; return zero vector
            return vec![0.0; self.len()];
        }
        let inv_norm = SimdUnit::splat(1.0 / norm);

        let mut out = Vec::with_capacity(self.len());

        for chunk in self.chunks_exact(SIMD_LANECOUNT) {
            let v = SimdUnit::from_slice(chunk);
            let scaled = v * inv_norm;
            out.extend_from_slice(&scaled.to_array());
        }

        out
    }
}