use super::*;
use crate::{StridedView, StridedViewMut};
fn reference_copy_scale(
dst: &mut [f64],
src: &[f64],
dims: &[usize],
dst_strides: &[isize],
src_strides: &[isize],
scale: f64,
) {
let mut dest_view = StridedViewMut::new(dst, dims, dst_strides, 0).unwrap();
let src_view: StridedView<'_, f64> = StridedView::new(src, dims, src_strides, 0).unwrap();
copy_scale(&mut dest_view, &src_view, scale).unwrap();
}
#[test]
fn raw_copy_scale_matches_view_kernel() {
let dims = [2usize, 3, 2];
let src_strides = [1isize, 2, 6];
let dst_strides = [6isize, 2, 1];
let src: Vec<f64> = (0..12).map(|value| value as f64 - 3.0).collect();
let mut expected = vec![0.0; 12];
reference_copy_scale(&mut expected, &src, &dims, &dst_strides, &src_strides, 1.5);
let mut actual = vec![0.0; 12];
let mut dest = RawStridedMut::new(&mut actual, &dims, &dst_strides, 0).unwrap();
let source = RawStridedRef::new(&src, &dims, &src_strides, 0).unwrap();
copy_scale_raw(&mut dest, &source, 1.5).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn raw_axpy_accumulates() {
let dims = [4usize];
let strides = [1isize];
let src = [1.0f64, 2.0, 3.0, 4.0];
let mut dst = [10.0f64, 20.0, 30.0, 40.0];
let mut dest = RawStridedMut::new(&mut dst, &dims, &strides, 0).unwrap();
let source = RawStridedRef::new(&src, &dims, &strides, 0).unwrap();
axpy_raw(&mut dest, &source, 2.0).unwrap();
assert_eq!(dst, [12.0, 24.0, 36.0, 48.0]);
}
#[test]
fn raw_copy_scale_conjugates_complex_sources() {
use num_complex::Complex64;
let dims = [2usize];
let strides = [1isize];
let src = [Complex64::new(1.0, 2.0), Complex64::new(-3.0, 4.0)];
let mut dst = [Complex64::new(0.0, 0.0); 2];
let mut dest = RawStridedMut::new(&mut dst, &dims, &strides, 0).unwrap();
let source = RawStridedRef::new(&src, &dims, &strides, 0).unwrap();
copy_scale_conj_raw(&mut dest, &source, Complex64::new(2.0, 0.0)).unwrap();
assert_eq!(dst[0], Complex64::new(2.0, -4.0));
assert_eq!(dst[1], Complex64::new(-6.0, -8.0));
}
fn reference_strided_copy(
dst: &mut [i64],
src: &[i64],
dims: &[usize],
dst_strides: &[isize],
dst_offset: isize,
src_strides: &[isize],
src_offset: isize,
) {
let total: usize = dims.iter().product();
for linear in 0..total {
let mut rest = linear;
let mut d = dst_offset;
let mut s = src_offset;
for axis in 0..dims.len() {
let coord = (rest % dims[axis]) as isize;
rest /= dims[axis];
d += coord * dst_strides[axis];
s += coord * src_strides[axis];
}
dst[d as usize] = src[s as usize];
}
}
#[test]
fn fused_range_replay_matches_reference_for_every_split() {
type Case = (
Vec<usize>,
Vec<isize>,
isize,
Vec<isize>,
isize,
usize,
usize,
);
let cases: Vec<Case> = vec![
(vec![7], vec![1], 0, vec![1], 0, 7, 7),
(vec![7], vec![1], 0, vec![-1], 6, 7, 7),
(vec![5, 3], vec![1, 5], 0, vec![2, 12], 1, 15, 40),
(vec![3, 4], vec![4, 1], 0, vec![1, 3], 0, 12, 12),
(vec![3, 2, 3], vec![1, 3, 6], 2, vec![1, -3, 6], 3, 20, 18),
(
vec![2, 3, 2],
vec![1, 2, 6],
0,
vec![-1, -2, -6],
11,
12,
12,
),
(vec![4, 3], vec![1, 4], 0, vec![0, 1], 0, 12, 3),
];
for (dims, dst_strides, dst_offset, src_strides, src_offset, dst_len, src_len) in cases {
let src: Vec<i64> = (0..src_len as i64).map(|v| v * 10 + 1).collect();
let mut expected = vec![-1i64; dst_len];
reference_strided_copy(
&mut expected,
&src,
&dims,
&dst_strides,
dst_offset,
&src_strides,
src_offset,
);
let layout = fuse_pair_layout(&dims, &dst_strides, &src_strides).unwrap();
let total = fused_total(&layout);
assert_eq!(total, dims.iter().product::<usize>());
let mut serial = vec![-1i64; dst_len];
{
let mut dest =
RawStridedMut::new(&mut serial, &dims, &dst_strides, dst_offset).unwrap();
let source = RawStridedRef::new(&src, &dims, &src_strides, src_offset).unwrap();
apply_fused_pair(&mut dest, &source, &layout, |d, v| *d = v, |v| v);
}
assert_eq!(serial, expected, "serial replay for dims {dims:?}");
for first in 0..=total {
for second in first..=total {
let mut actual = vec![-1i64; dst_len];
for (start, end) in [(0, first), (first, second), (second, total)] {
unsafe {
apply_fused_range(
actual.as_mut_ptr(),
dst_offset,
src.as_ptr(),
src_offset,
&layout,
start,
end - start,
&|d: &mut i64, v| *d = v,
&|v| v,
);
}
}
assert_eq!(actual, expected, "dims {dims:?} split at {first}, {second}");
}
}
}
}