#[allow(unused_imports)]
use crate::prelude::*;
const CHUNK: usize = 4;
pub fn simd_add_i64(a: &[i64], b: &[i64]) -> Vec<i64> {
let out_len = a.len().max(b.len());
let mut result = vec![0i64; out_len];
let common = a.len().min(b.len());
let chunks = common / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
result[base] = a[base] + b[base];
result[base + 1] = a[base + 1] + b[base + 1];
result[base + 2] = a[base + 2] + b[base + 2];
result[base + 3] = a[base + 3] + b[base + 3];
}
let tail_start = chunks * CHUNK;
for i in tail_start..common {
result[i] = a[i] + b[i];
}
if a.len() > common {
result[common..out_len].copy_from_slice(&a[common..]);
} else if b.len() > common {
result[common..out_len].copy_from_slice(&b[common..]);
}
result
}
pub fn simd_sub_i64(a: &[i64], b: &[i64]) -> Vec<i64> {
let out_len = a.len().max(b.len());
let mut result = vec![0i64; out_len];
let common = a.len().min(b.len());
let chunks = common / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
result[base] = a[base] - b[base];
result[base + 1] = a[base + 1] - b[base + 1];
result[base + 2] = a[base + 2] - b[base + 2];
result[base + 3] = a[base + 3] - b[base + 3];
}
let tail_start = chunks * CHUNK;
for i in tail_start..common {
result[i] = a[i] - b[i];
}
if a.len() > common {
result[common..out_len].copy_from_slice(&a[common..]);
} else if b.len() > common {
for i in common..out_len {
result[i] = -b[i];
}
}
result
}
pub fn simd_scalar_mul_i64(coeffs: &[i64], scalar: i64) -> Vec<i64> {
let mut result = vec![0i64; coeffs.len()];
let chunks = coeffs.len() / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
result[base] = coeffs[base] * scalar;
result[base + 1] = coeffs[base + 1] * scalar;
result[base + 2] = coeffs[base + 2] * scalar;
result[base + 3] = coeffs[base + 3] * scalar;
}
let tail_start = chunks * CHUNK;
for i in tail_start..coeffs.len() {
result[i] = coeffs[i] * scalar;
}
result
}
pub fn simd_coeffs_equal(a: &[i64], b: &[i64]) -> bool {
let common = a.len().min(b.len());
let chunks = common / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
if a[base] != b[base]
|| a[base + 1] != b[base + 1]
|| a[base + 2] != b[base + 2]
|| a[base + 3] != b[base + 3]
{
return false;
}
}
let tail_start = chunks * CHUNK;
for i in tail_start..common {
if a[i] != b[i] {
return false;
}
}
let longer = if a.len() > b.len() { a } else { b };
for &v in &longer[common..] {
if v != 0 {
return false;
}
}
true
}
pub fn simd_eval_horner(coeffs: &[i64], x: i64) -> i64 {
if coeffs.is_empty() {
return 0;
}
let mut acc = 0i64;
for &c in coeffs.iter().rev() {
acc = acc.wrapping_mul(x).wrapping_add(c);
}
acc
}
pub fn simd_dot_product_i64(a: &[i64], b: &[i64]) -> Option<i128> {
if a.len() != b.len() {
return None;
}
let mut acc: i128 = 0;
let chunks = a.len() / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
acc += (a[base] as i128) * (b[base] as i128);
acc += (a[base + 1] as i128) * (b[base + 1] as i128);
acc += (a[base + 2] as i128) * (b[base + 2] as i128);
acc += (a[base + 3] as i128) * (b[base + 3] as i128);
}
let tail_start = chunks * CHUNK;
for i in tail_start..a.len() {
acc += (a[i] as i128) * (b[i] as i128);
}
Some(acc)
}
pub fn simd_convolve_i64(a: &[i64], b: &[i64]) -> Vec<i64> {
if a.is_empty() || b.is_empty() {
return Vec::new();
}
let out_len = a.len() + b.len() - 1;
let mut result = vec![0i64; out_len];
const TILE: usize = 64;
for b_start in (0..b.len()).step_by(TILE) {
let b_end = (b_start + TILE).min(b.len());
for (i, &ai) in a.iter().enumerate() {
let mut j = b_start;
let tile_chunks = (b_end - b_start) / CHUNK;
for _ in 0..tile_chunks {
result[i + j] += ai * b[j];
result[i + j + 1] += ai * b[j + 1];
result[i + j + 2] += ai * b[j + 2];
result[i + j + 3] += ai * b[j + 3];
j += CHUNK;
}
while j < b_end {
result[i + j] += ai * b[j];
j += 1;
}
}
}
result
}
pub fn simd_negate_i64(coeffs: &[i64]) -> Vec<i64> {
let mut result = vec![0i64; coeffs.len()];
let chunks = coeffs.len() / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
result[base] = -coeffs[base];
result[base + 1] = -coeffs[base + 1];
result[base + 2] = -coeffs[base + 2];
result[base + 3] = -coeffs[base + 3];
}
let tail_start = chunks * CHUNK;
for i in tail_start..coeffs.len() {
result[i] = -coeffs[i];
}
result
}
pub fn simd_linf_norm(coeffs: &[i64]) -> u64 {
if coeffs.is_empty() {
return 0;
}
let mut max_val: u64 = 0;
let chunks = coeffs.len() / CHUNK;
for chunk_idx in 0..chunks {
let base = chunk_idx * CHUNK;
let a0 = (coeffs[base] as i128).unsigned_abs() as u64;
let a1 = (coeffs[base + 1] as i128).unsigned_abs() as u64;
let a2 = (coeffs[base + 2] as i128).unsigned_abs() as u64;
let a3 = (coeffs[base + 3] as i128).unsigned_abs() as u64;
max_val = max_val.max(a0).max(a1).max(a2).max(a3);
}
let tail_start = chunks * CHUNK;
for &c in &coeffs[tail_start..] {
max_val = max_val.max((c as i128).unsigned_abs() as u64);
}
max_val
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simd_add_i64_same_len() {
let a = vec![1, 2, 3, 4, 5, 6, 7, 8];
let b = vec![10, 20, 30, 40, 50, 60, 70, 80];
let r = simd_add_i64(&a, &b);
assert_eq!(r, vec![11, 22, 33, 44, 55, 66, 77, 88]);
}
#[test]
fn test_simd_add_i64_different_len() {
let a = vec![1, 2, 3];
let b = vec![10, 20, 30, 40, 50];
let r = simd_add_i64(&a, &b);
assert_eq!(r, vec![11, 22, 33, 40, 50]);
}
#[test]
fn test_simd_sub_i64() {
let a = vec![10, 20, 30, 40];
let b = vec![1, 2, 3, 4];
let r = simd_sub_i64(&a, &b);
assert_eq!(r, vec![9, 18, 27, 36]);
}
#[test]
fn test_simd_sub_i64_shorter_a() {
let a = vec![10, 20];
let b = vec![1, 2, 3, 4];
let r = simd_sub_i64(&a, &b);
assert_eq!(r, vec![9, 18, -3, -4]);
}
#[test]
fn test_simd_scalar_mul_i64() {
let coeffs = vec![1, 2, 3, 4, 5];
let r = simd_scalar_mul_i64(&coeffs, 3);
assert_eq!(r, vec![3, 6, 9, 12, 15]);
}
#[test]
fn test_simd_coeffs_equal_same() {
let a = vec![1, 2, 3, 4];
let b = vec![1, 2, 3, 4];
assert!(simd_coeffs_equal(&a, &b));
}
#[test]
fn test_simd_coeffs_equal_trailing_zeros() {
let a = vec![1, 2, 3];
let b = vec![1, 2, 3, 0, 0];
assert!(simd_coeffs_equal(&a, &b));
}
#[test]
fn test_simd_coeffs_equal_different() {
let a = vec![1, 2, 3, 4];
let b = vec![1, 2, 3, 5];
assert!(!simd_coeffs_equal(&a, &b));
}
#[test]
fn test_simd_eval_horner() {
let coeffs = vec![1, 2, 3];
assert_eq!(simd_eval_horner(&coeffs, 2), 17);
}
#[test]
fn test_simd_eval_horner_empty() {
assert_eq!(simd_eval_horner(&[], 42), 0);
}
#[test]
fn test_simd_dot_product_i64() {
let a = vec![1, 2, 3, 4];
let b = vec![5, 6, 7, 8];
assert_eq!(simd_dot_product_i64(&a, &b), Some(70));
}
#[test]
fn test_simd_dot_product_mismatched() {
let a = vec![1, 2];
let b = vec![1, 2, 3];
assert_eq!(simd_dot_product_i64(&a, &b), None);
}
#[test]
fn test_simd_convolve_i64() {
let a = vec![1, 2];
let b = vec![3, 4];
let r = simd_convolve_i64(&a, &b);
assert_eq!(r, vec![3, 10, 8]);
}
#[test]
fn test_simd_convolve_larger() {
let a = vec![1, 1, 1];
let b = vec![1, 1];
let r = simd_convolve_i64(&a, &b);
assert_eq!(r, vec![1, 2, 2, 1]);
}
#[test]
fn test_simd_convolve_empty() {
assert!(simd_convolve_i64(&[], &[1, 2]).is_empty());
assert!(simd_convolve_i64(&[1], &[]).is_empty());
}
#[test]
fn test_simd_negate_i64() {
let coeffs = vec![1, -2, 3, -4, 5];
let r = simd_negate_i64(&coeffs);
assert_eq!(r, vec![-1, 2, -3, 4, -5]);
}
#[test]
fn test_simd_linf_norm() {
let coeffs = vec![1, -7, 3, -4, 5];
assert_eq!(simd_linf_norm(&coeffs), 7);
}
#[test]
fn test_simd_linf_norm_empty() {
assert_eq!(simd_linf_norm(&[]), 0);
}
#[test]
fn test_large_add() {
let a: Vec<i64> = (0..100).collect();
let b: Vec<i64> = (100..200).collect();
let r = simd_add_i64(&a, &b);
for (i, &item) in r.iter().enumerate() {
assert_eq!(item, (i as i64) + (100 + i as i64));
}
}
#[test]
fn test_large_scalar_mul() {
let coeffs: Vec<i64> = (1..=50).collect();
let r = simd_scalar_mul_i64(&coeffs, -2);
for (i, &v) in r.iter().enumerate() {
assert_eq!(v, -2 * (i as i64 + 1));
}
}
}