use crate::kernels::*;
use proptest::collection::vec;
use proptest::prelude::*;
fn any_f64() -> impl Strategy<Value = f64> {
-1e3_f64..1e3_f64
}
fn any_f32() -> impl Strategy<Value = f32> {
-1e2_f32..1e2_f32
}
fn close_f64(a: f64, b: f64) -> bool {
let tol = 1e-12 * (1.0 + a.abs().max(b.abs()));
(a - b).abs() < tol
}
proptest! {
#[test]
fn dot_f64_commutative(a in vec(any_f64(), 1usize..100), b in vec(any_f64(), 1usize..100)) {
let len = a.len().min(b.len());
let ab = dot_scalar(&a[..len], &b[..len]);
let ba = dot_scalar(&b[..len], &a[..len]);
assert!(close_f64(ab, ba), "commutativity: {} != {}", ab, ba);
}
#[test]
fn dot_f64_variants_agree(a in vec(any_f64(), 1usize..100), b in vec(any_f64(), 1usize..100)) {
let len = a.len().min(b.len());
let expected = dot_scalar(&a[..len], &b[..len]);
let neon = dot_neon(&a[..len], &b[..len]);
assert!(close_f64(expected, neon), "scalar={} != neon={}", expected, neon);
}
#[test]
fn dot_f32_variants_agree(a in vec(any_f32(), 1usize..50), b in vec(any_f32(), 1usize..50)) {
let len = a.len().min(b.len());
let expected = dot_f32_scalar(&a[..len], &b[..len]);
let neon = dot_f32_neon(&a[..len], &b[..len]);
let f32_tol = 1e-3 * (1.0 + expected.abs().max(neon.abs()));
assert!((expected - neon).abs() < f32_tol, "scalar={} != neon={}", expected, neon);
}
}
proptest! {
#[test]
fn reduce_sum_additive(v in vec(any_f64(), 2usize..100)) {
let mid = v.len() / 2;
let total = reduce_sum_scalar(&v);
let left = reduce_sum_scalar(&v[..mid]);
let right = reduce_sum_scalar(&v[mid..]);
assert!(close_f64(total, left + right),
"reduce_sum not additive: total={} left={} right={}", total, left, right);
}
}
proptest! {
#[test]
fn reduce_max_properties(v in vec(any_f64(), 1usize..100)) {
if v.is_empty() { return Ok(()); }
let mx = reduce_max_scalar(&v);
assert!(v.iter().all(|&x| x <= mx + 1e-12),
"reduce_max {} not >= all elements", mx);
assert!(v.iter().any(|&x| close_f64(x, mx)),
"reduce_max {} not found in vector", mx);
}
#[test]
fn abs_max_noneg(v in vec(any_f64(), 1usize..100)) {
if v.is_empty() { return Ok(()); }
let am = abs_max_f64_scalar(&v);
assert!(am >= 0.0, "abs_max {} is negative", am);
assert!(v.iter().all(|&x| x.abs() <= am + 1e-12),
"abs_max {} less than |x| for some element", am);
}
}
proptest! {
#[test]
fn argmax_index(v in vec(any_f64(), 1usize..100)) {
if v.is_empty() { return Ok(()); }
let idx = argmax_f64_scalar(&v);
assert!(idx < v.len(), "argmax {} out of bounds", idx);
let mx = reduce_max_scalar(&v);
assert!(close_f64(v[idx], mx), "argmax value {} != max {}", v[idx], mx);
}
}
proptest! {
#[test]
fn softmax_sums_to_one(v in vec(any_f64(), 1usize..50)) {
if v.is_empty() { return Ok(()); }
let mut out = vec![0.0; v.len()];
softmax_scalar(&v, &mut out);
let sum: f64 = out.iter().sum();
assert!((sum - 1.0).abs() < 1e-10, "softmax sum={} != 1", sum);
assert!(out.iter().all(|&x| x >= 0.0), "softmax output < 0");
}
}
proptest! {
#[test]
fn euclidean_nonnegative(a in vec(any_f64(), 1usize..100), b in vec(any_f64(), 1usize..100)) {
let len = a.len().min(b.len());
let d = euclidean_distance_f64_scalar(&a[..len], &b[..len]);
assert!(d >= 0.0, "euclidean distance < 0: {}", d);
}
#[test]
fn euclidean_triangle_inequality(
a in vec(any_f64(), 5usize..50),
b in vec(any_f64(), 5usize..50),
c in vec(any_f64(), 5usize..50)
) {
let len = a.len().min(b.len()).min(c.len());
let ab = euclidean_distance_f64_scalar(&a[..len], &b[..len]);
let bc = euclidean_distance_f64_scalar(&b[..len], &c[..len]);
let ac = euclidean_distance_f64_scalar(&a[..len], &c[..len]);
assert!(ac <= ab + bc + 1e-9,
"triangle inequality: {} > {} + {}", ac, ab, bc);
}
#[test]
fn euclidean_zero_for_equal(a in vec(any_f64(), 1usize..50)) {
let d = euclidean_distance_f64_scalar(&a, &a);
assert!(d < 1e-12, "identical vectors distance={}", d);
}
#[test]
fn euclidean_simd_agrees(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let expected = euclidean_distance_f64_scalar(&a[..len], &b[..len]);
let neon = euclidean_distance_f64_neon(&a[..len], &b[..len]);
assert!(close_f64(expected, neon),
"euclidean scalar={} != neon={}", expected, neon);
}
}
proptest! {
#[test]
fn cosine_range(a in vec(any_f64(), 2usize..50), b in vec(any_f64(), 2usize..50)) {
let len = a.len().min(b.len());
let c = cosine_similarity_f64_scalar(&a[..len], &b[..len]);
assert!(c >= -1.0 - 1e-9 && c <= 1.0 + 1e-9,
"cosine {} outside [-1,1]", c);
}
#[test]
fn cosine_parallel_is_one(a in vec(any_f64(), 2usize..50)) {
let c = cosine_similarity_f64_scalar(&a, &a);
assert!(close_f64(c, 1.0), "self cosine {} != 1", c);
}
}
proptest! {
#[test]
fn hadamard_agrees(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let mut c_scalar = vec![0.0; len];
let mut c_neon = vec![0.0; len];
hadamard_product_f64_scalar(&a[..len], &b[..len], &mut c_scalar);
hadamard_product_f64_neon(&a[..len], &b[..len], &mut c_neon);
for i in 0..len {
assert!(close_f64(c_scalar[i], c_neon[i]),
"hadamard[{}] scalar={} neon={}", i, c_scalar[i], c_neon[i]);
}
}
#[test]
fn add_scalar_correct(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let mut c = vec![0.0; len];
add_f64_scalar(&a[..len], &b[..len], &mut c);
for i in 0..len {
assert!(close_f64(c[i], a[i] + b[i]),
"add[{}] got {} expected {}", i, c[i], a[i] + b[i]);
}
}
#[test]
fn sub_scalar_correct(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let mut c = vec![0.0; len];
sub_f64_scalar(&a[..len], &b[..len], &mut c);
for i in 0..len {
assert!(close_f64(c[i], a[i] - b[i]),
"sub[{}] got {} expected {}", i, c[i], a[i] - b[i]);
}
}
#[test]
fn mul_scalar_correct(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let mut c = vec![0.0; len];
mul_f64_scalar(&a[..len], &b[..len], &mut c);
for i in 0..len {
assert!(close_f64(c[i], a[i] * b[i]),
"mul[{}] got {} expected {}", i, c[i], a[i] * b[i]);
}
}
}
proptest! {
#[test]
fn negate_double_negation(v in vec(any_f64(), 1usize..100)) {
if v.is_empty() { return Ok(()); }
let mut tmp = vec![0.0; v.len()];
let mut back = vec![0.0; v.len()];
negate_f64_scalar(&v, &mut tmp);
negate_f64_scalar(&tmp, &mut back);
for i in 0..v.len() {
assert!(close_f64(back[i], v[i]),
"negate[{}] expected {} got {}", i, v[i], back[i]);
}
}
#[test]
fn negate_simd_agrees(v in vec(any_f64(), 1usize..100)) {
if v.is_empty() { return Ok(()); }
let mut scalar = vec![0.0; v.len()];
let mut neon = vec![0.0; v.len()];
negate_f64_scalar(&v, &mut scalar);
negate_f64_neon(&v, &mut neon);
for i in 0..v.len() {
assert!(close_f64(scalar[i], neon[i]),
"negate[{}] scalar={} neon={}", i, scalar[i], neon[i]);
}
}
}
proptest! {
#[test]
fn clamp_bounds(
a in vec(any_f64(), 1usize..100),
lo in any_f64(),
hi in any_f64()
) {
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
let mut c = vec![0.0; a.len()];
clamp_f64_scalar(&a, lo, hi, &mut c);
for i in 0..a.len() {
assert!(c[i] >= lo - 1e-12, "clamp[{}] {} < lo {}", i, c[i], lo);
assert!(c[i] <= hi + 1e-12, "clamp[{}] {} > hi {}", i, c[i], hi);
}
}
#[test]
fn clamp_simd_agrees(
a in vec(any_f64(), 1usize..100),
lo in any_f64(),
hi in any_f64()
) {
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
let mut c_scalar = vec![0.0; a.len()];
let mut c_neon = vec![0.0; a.len()];
clamp_f64_scalar(&a, lo, hi, &mut c_scalar);
clamp_f64_neon(&a, lo, hi, &mut c_neon);
for i in 0..a.len() {
assert!(close_f64(c_scalar[i], c_neon[i]),
"clamp[{}] scalar={} neon={}", i, c_scalar[i], c_neon[i]);
}
}
}
proptest! {
#[test]
fn memchr_correctness(data in vec(any::<u8>(), 0usize..100)) {
if data.is_empty() { return Ok(()); }
let byte = data[0];
let result = memchr_scalar(byte, &data);
assert!(result.is_some(), "memchr failed to find byte {}", byte);
assert_eq!(data[result.unwrap()], byte,
"memchr returned wrong index {}", result.unwrap());
}
#[test]
fn memchr_not_found(data in vec(any::<u8>(), 1usize..50)) {
let byte = if data.is_empty() || data[0] == 255 { 0 } else { 255 };
if data.contains(&byte) { return Ok(()); }
let result = memchr_scalar(byte, &data);
assert!(result.is_none(), "memchr found absent byte at {:?}", result);
}
#[test]
fn memchr_simd_agrees(data in vec(any::<u8>(), 0usize..100), byte in any::<u8>()) {
let expected = memchr_scalar(byte, &data);
let neon = memchr_neon(byte, &data);
assert_eq!(expected, neon, "memchr scalar={:?} != neon={:?}", expected, neon);
}
}
proptest! {
#[test]
fn matmul_identity(n in 1usize..10) {
let size = n;
let mut a = vec![0.0; size * size];
for i in 0..size { a[i * size + i] = 1.0; }
let b = vec![1.0; size * size];
let mut c = vec![0.0; size * size];
matmul_scalar(&a, &b, &mut c, size);
for i in 0..size {
for j in 0..size {
let expected = b[i * size + j];
assert!(close_f64(c[i * size + j], expected),
"matmul identity[{}][{}] expected {} got {}",
i, j, expected, c[i * size + j]);
}
}
}
#[test]
fn matmul_simd_agrees(
a in vec(any_f64(), 1usize..36),
b in vec(any_f64(), 1usize..36),
n in 1usize..6
) {
let size = n;
if a.len() < size * size || b.len() < size * size { return Ok(()); }
let mut c_scalar = vec![0.0; size * size];
let mut c_neon = vec![0.0; size * size];
matmul_scalar(&a[..size*size], &b[..size*size], &mut c_scalar, size);
matmul_neon(&a[..size*size], &b[..size*size], &mut c_neon, size);
for i in 0..size*size {
assert!(close_f64(c_scalar[i], c_neon[i]),
"matmul[{}] scalar={} neon={}", i, c_scalar[i], c_neon[i]);
}
}
}
proptest! {
#[test]
fn dot_clamp_reduce_agrees(
a in vec(any_f64(), 1usize..100),
b in vec(any_f64(), 1usize..100)
) {
let len = a.len().min(b.len());
let expected = dot_clamp_reduce_scalar(&a[..len], &b[..len]);
let neon = dot_clamp_reduce_neon(&a[..len], &b[..len]);
assert!(close_f64(expected, neon),
"fused scalar={} != neon={}", expected, neon);
}
}
#[test]
fn proptest_module_active() {
assert!(true);
}