use trueno::simulation::{BackendCategory, BackendSelector, BackendTolerance};
use trueno::{
select_backend_for_operation, select_best_available_backend, Backend, OperationType, Vector,
};
#[test]
fn test_a001_scalar_bit_exact() {
let a = Vector::from_slice(&[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
let b = Vector::from_slice(&[8.0f32, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0]);
let mut first_result: Option<Vec<f32>> = None;
for _ in 0..1000 {
let result = a.add(&b).expect("add failed");
if let Some(ref first) = first_result {
for (i, (r, f)) in result.as_slice().iter().zip(first.iter()).enumerate() {
assert_eq!(
r.to_bits(),
f.to_bits(),
"A-001 FALSIFIED: Scalar not bit-exact at index {i}"
);
}
} else {
first_result = Some(result.as_slice().to_vec());
}
}
}
#[test]
fn test_a002_backend_consistent_add() {
let a = Vector::from_slice(&[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
let b = Vector::from_slice(&[8.0f32, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0]);
let result1 = a.add(&b).expect("add failed");
let result2 = a.add(&b).expect("add failed");
for (i, (r1, r2)) in result1.as_slice().iter().zip(result2.as_slice().iter()).enumerate() {
assert_eq!(
r1.to_bits(),
r2.to_bits(),
"A-002 FALSIFIED: Results differ at index {i}: {r1} != {r2}"
);
}
}
#[test]
fn test_a005_gpu_threshold() {
let selector = BackendSelector::default();
assert_eq!(
selector.select_for_size(99_999, true),
BackendCategory::SimdParallel,
"A-005 FALSIFIED: 99,999 elements should use SIMD+Parallel, not GPU"
);
assert_eq!(
selector.select_for_size(100_000, true),
BackendCategory::Gpu,
"A-005 FALSIFIED: 100,000 elements should use GPU"
);
}
#[test]
fn test_a006_parallel_threshold() {
let selector = BackendSelector::default();
assert_eq!(
selector.select_for_size(999, false),
BackendCategory::SimdOnly,
"A-006 FALSIFIED: 999 elements should use SIMD only, not parallel"
);
assert_eq!(
selector.select_for_size(1_000, false),
BackendCategory::SimdParallel,
"A-006 FALSIFIED: 1,000 elements should use SIMD+Parallel"
);
}
#[test]
fn test_a007_gpu_fallback() {
let selector = BackendSelector::default();
assert_eq!(
selector.select_for_size(1_000_000, false),
BackendCategory::SimdParallel,
"A-007 FALSIFIED: Should fallback to SIMD+Parallel when GPU unavailable"
);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_a008_simd_auto_detect() {
let backend = select_best_available_backend();
assert!(
matches!(backend, Backend::SSE2 | Backend::AVX | Backend::AVX2 | Backend::AVX512),
"A-008 FALSIFIED: x86_64 should detect SIMD variant, got {:?}",
backend
);
}
#[test]
fn test_a009_backend_selection_deterministic() {
let selector = BackendSelector::default();
for size in [100, 1_000, 10_000, 100_000, 1_000_000] {
let first = selector.select_for_size(size, true);
for _ in 0..1000 {
let result = selector.select_for_size(size, true);
assert_eq!(
result, first,
"A-009 FALSIFIED: Backend selection not deterministic for size {size}"
);
}
}
}
#[test]
fn test_a010_backend_selection_performance() {
use std::time::Instant;
let selector = BackendSelector::default();
let iterations = 100_000;
let start = Instant::now();
for size in (0..iterations).map(|i| i * 100) {
let _ = selector.select_for_size(size, true);
}
let elapsed = start.elapsed();
let avg_ns = elapsed.as_nanos() / iterations as u128;
assert!(
avg_ns < 1_000, "A-010 FALSIFIED: Backend selection took {avg_ns}ns average, expected < 1000ns"
);
}
#[test]
fn test_a011_gpu_transfer_amortization() {
let selector = BackendSelector::default();
for size in [100_000, 500_000, 1_000_000, 10_000_000] {
let category = selector.select_for_size(size, true);
assert_eq!(
category,
BackendCategory::Gpu,
"A-011 FALSIFIED: Size {size} should select GPU"
);
}
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_a012_avx512_selection_for_compute_bound() {
let backend = select_backend_for_operation(OperationType::ComputeBound);
if is_x86_feature_detected!("avx512f") {
assert_eq!(
backend,
Backend::AVX512,
"A-012 FALSIFIED: ComputeBound ops should use AVX-512 when available"
);
} else {
assert!(
matches!(backend, Backend::AVX2 | Backend::AVX | Backend::SSE2),
"A-012: Without AVX-512, should use best available SIMD"
);
}
}
#[test]
fn test_a003_avx512_matches_scalar() {
let a = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let b = vec![0.5f32, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5];
let va = Vector::from_slice(&a);
let vb = Vector::from_slice(&b);
let result_add = va.add(&vb).expect("add failed");
for (i, (r, (&x, &y))) in result_add.as_slice().iter().zip(a.iter().zip(b.iter())).enumerate() {
let expected = x + y;
assert!(
(*r - expected).abs() < f32::EPSILON,
"A-003 FALSIFIED: Add result differs at index {i}: {} vs {}",
r,
expected
);
}
let result_mul = va.mul(&vb).expect("mul failed");
for (i, (r, (&x, &y))) in result_mul.as_slice().iter().zip(a.iter().zip(b.iter())).enumerate() {
let expected = x * y;
assert!(
(*r - expected).abs() < f32::EPSILON,
"A-003 FALSIFIED: Mul result differs at index {i}: {} vs {}",
r,
expected
);
}
}
#[test]
fn test_a004_gpu_tolerance() {
let tolerance = BackendTolerance::relaxed();
let gpu_tolerance = tolerance.for_backends(Backend::GPU, Backend::Scalar);
assert!(
gpu_tolerance <= 1e-4, "A-004 FALSIFIED: GPU tolerance ({}) exceeds 1e-4",
gpu_tolerance
);
let scalar_result = [1.0f32, 2.0, 3.0, 4.0];
let gpu_result = [1.00009f32, 2.00009, 3.00009, 4.00009];
for (i, (s, g)) in scalar_result.iter().zip(gpu_result.iter()).enumerate() {
let diff = (s - g).abs();
assert!(
diff <= gpu_tolerance,
"A-004 FALSIFIED: GPU differs from scalar by {} at index {i} (tolerance: {})",
diff,
gpu_tolerance
);
}
}
#[test]
#[cfg(target_arch = "aarch64")]
fn test_a013_neon_speedup() {
use std::time::Instant;
let size = 10_000;
let a: Vec<f32> = (0..size).map(|i| i as f32).collect();
let b: Vec<f32> = (0..size).map(|i| (i * 2) as f32).collect();
let start = Instant::now();
for _ in 0..100 {
let _: Vec<f32> = a.iter().zip(b.iter()).map(|(x, y)| x + y).collect();
}
let scalar_time = start.elapsed();
let va = Vector::from_slice(&a);
let vb = Vector::from_slice(&b);
let start = Instant::now();
for _ in 0..100 {
let _ = va.add(&vb);
}
let neon_time = start.elapsed();
let speedup = scalar_time.as_nanos() as f64 / neon_time.as_nanos() as f64;
assert!(speedup >= 2.0, "A-013 FALSIFIED: NEON speedup {} is less than 2x", speedup);
}
#[test]
#[cfg(not(target_arch = "aarch64"))]
fn test_a013_neon_speedup_placeholder() {
}
#[test]
#[cfg(target_arch = "wasm32")]
fn test_a014_wasm_simd_speedup() {
let size = 1_000;
let a: Vec<f32> = (0..size).map(|i| i as f32).collect();
let b: Vec<f32> = (0..size).map(|i| (i * 2) as f32).collect();
let va = Vector::from_slice(&a);
let vb = Vector::from_slice(&b);
let result = va.add(&vb).expect("WASM SIMD add failed");
for (i, (r, (&x, &y))) in result.as_slice().iter().zip(a.iter().zip(b.iter())).enumerate() {
let expected = x + y;
assert!(
(*r - expected).abs() < f32::EPSILON,
"A-014 FALSIFIED: WASM SIMD result differs at index {i}"
);
}
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_a014_wasm_simd_placeholder() {
}
#[test]
fn test_a015_gpu_for_large_workloads() {
let selector = BackendSelector::default();
for size in [1_000_000, 10_000_000] {
let category = selector.select_for_size(size, true);
assert_eq!(
category,
BackendCategory::Gpu,
"A-015 FALSIFIED: Size {size} should use GPU for best performance"
);
}
}
#[test]
fn test_backend_tolerance_defaults() {
let tolerance = BackendTolerance::default();
assert_eq!(tolerance.scalar_vs_simd, 0.0);
assert!((tolerance.simd_vs_gpu - 1e-5).abs() < 1e-10);
assert!((tolerance.gpu_vs_gpu - 1e-6).abs() < 1e-10);
}
#[test]
fn test_backend_tolerance_for_backends() {
let tolerance = BackendTolerance::default();
assert_eq!(tolerance.for_backends(Backend::Scalar, Backend::Scalar), 0.0);
assert_eq!(tolerance.for_backends(Backend::Scalar, Backend::AVX2), 0.0);
assert_eq!(tolerance.for_backends(Backend::GPU, Backend::GPU), tolerance.gpu_vs_gpu);
assert_eq!(tolerance.for_backends(Backend::AVX2, Backend::GPU), tolerance.simd_vs_gpu);
}