lightweight-simd 0.1.1

Lightweight generic fixed-array vector types relying on compiler auto-vectorization
Documentation
// Most or all contents generated by AI (model glm-5.3)

//! Tests for the enforced-alignment wrappers and aligned aliases: layout
//! guarantees, delegation equivalence against plain `Simd`, conversions, and
//! mask handling.

use num_traits::Zero as _;

use lightweight_simd::*;

#[test]
fn alignment_and_size() {
    use core::mem::{align_of, size_of};
    assert_eq!(align_of::<f64x8>(), 64);
    assert_eq!(size_of::<f64x8>(), 64);
    assert_eq!(align_of::<f64x4>(), 32);
    assert_eq!(align_of::<f64x2>(), 16);
    assert_eq!(align_of::<f64x16>(), 64); // capped at 64
    assert_eq!(size_of::<f64x16>(), 128);
    assert_eq!(align_of::<f32x16>(), 64);
    assert_eq!(align_of::<f32x4>(), 16);
    assert_eq!(align_of::<f32x2>(), 8);
    assert_eq!(align_of::<i64x8>(), 64);
    assert_eq!(align_of::<i32x8>(), 32);
    assert_eq!(align_of::<i16x4>(), 8);
    assert_eq!(align_of::<i8x64>(), 64);
    // masks stay plain
    assert_eq!(align_of::<mask8>(), 1);
    // generic wrappers usable for any (T, N); size padded to alignment
    assert_eq!(align_of::<Aligned64<f64, 2>>(), 64);
    assert_eq!(size_of::<Aligned64<f64, 2>>(), 64);
}

#[test]
fn array_alignment() {
    let v: Vec<f64x8> = vec![f64x8::zero(); 16];
    assert_eq!(v.as_ptr() as usize % 64, 0);
    let w: Vec<f32x4> = vec![f32x4::zero(); 16];
    assert_eq!(w.as_ptr() as usize % 16, 0);
}

#[test]
fn ops_equivalence() {
    let a = f64x8::from_fn(|i| i as f64 * 0.5 + 1.0);
    let b = f64x8::from_fn(|i| i as f64 * 0.25 - 2.0);
    let got = (a * b).mul_add(a, b) + b * 2.0 - a;
    let a2: Simd<f64, 8> = a.into();
    let b2 = b.to_simd();
    let want = (a2 * b2).mul_add(a2, b2) + b2 * 2.0 - a2;
    assert_eq!(got.to_array(), want.to_array());
    assert_eq!(got.reduce_sum(), want.reduce_sum());

    // assigns and negation
    let mut c = a;
    c += b;
    c -= b;
    c *= b;
    c /= b;
    let mut d = a2;
    d += b2;
    d -= b2;
    d *= b2;
    d /= b2;
    assert_eq!(c.to_simd(), d);
    assert_eq!((-c).reduce_sum(), (-d).reduce_sum());
}

#[test]
fn conversions() {
    let s: Simd<f32, 4> = Simd::from_fn(|i| i as f32);
    let a: f32x4 = s.into();
    let back: Simd<f32, 4> = a.into();
    assert_eq!(back, s);
    assert_eq!(f32x4::from([1.0, 2.0, 3.0, 4.0]).reduce_sum(), 10.0);
    assert_eq!(a.to_simd(), s);
}

#[test]
fn mask_select_on_aligned() {
    let a = f64x8::splat(1.0);
    let b = f64x8::splat(2.0);
    let m: Simd<bool, 8> = Simd::from_fn(|i| i % 2 == 0);
    // per lane: 1, 2, 1, 2, 1, 2, 1, 2
    assert_eq!(a.mask_select(m, b).reduce_sum(), 12.0);
    // comparisons on aligned vectors produce plain masks
    let m2 = a.simd_lt(b);
    assert_eq!(m2.to_array(), [true; 8]);
    assert!(m2.all_true());
    assert_eq!(a.simd_eq(a).to_array(), [true; 8]);
    // mask_select also exists on plain Simd
    assert_eq!(Simd::<f64, 8>::splat(1.0).mask_select(m, Simd::splat(2.0)).reduce_sum(), 12.0);
}

#[test]
fn float_and_memory_delegation() {
    let v = f64x4::from([4.0, 9.0, 0.25, 2.25]);
    assert_eq!(v.sqrt().to_array(), [2.0, 3.0, 0.5, 1.5]);
    let mut buf = [0.0f64; 4];
    v.sqrt().store_slice(&mut buf);
    assert_eq!(buf, [2.0, 3.0, 0.5, 1.5]);
    let padded = f64x4::from_slice_pad(&[1.0, 2.0]);
    assert_eq!(padded.reduce_sum(), 3.0);
    assert_eq!(f64x4::from([1.5, 2.5, -1.5, -2.5]).round_ties_even().to_array(), [2.0, 2.0, -2.0, -2.0]);
}

#[test]
fn common_traits() {
    assert!(f64x8::zero().is_zero());
    assert_eq!(i64x4::one().reduce_product(), 1);
    assert_eq!(f64x4::default(), f64x4::zero());
    let z: f64x4 = num_traits::Zero::zero();
    assert!(z.is_zero());
    // bitwise/shift delegation on integer aliases
    let v = i64x4::from([1, 2, 3, 4]);
    assert_eq!((v << 2).to_array(), [4, 8, 12, 16]);
    assert_eq!((v & v).to_simd(), v.to_simd());
}

#[test]
fn uninit_scratch_aligned() {
    let mut v = unsafe { f64x8::uninit() };
    v.fill(1.5);
    assert_eq!(v.reduce_sum(), 12.0);
}

#[cfg(feature = "complex")]
#[test]
fn complex_alignment() {
    assert_eq!(core::mem::align_of::<lightweight_simd::c64x2>(), 32);
    assert_eq!(core::mem::align_of::<lightweight_simd::c64x4>(), 64);
}

#[cfg(feature = "half")]
#[test]
fn half_alignment() {
    assert_eq!(core::mem::align_of::<lightweight_simd::f16x2>(), 4);
    assert_eq!(core::mem::align_of::<lightweight_simd::f16x32>(), 64);
    assert_eq!(core::mem::align_of::<lightweight_simd::bf16x8>(), 16);
}