use crate::error::{Error, Result};
pub fn vector_reduce_product(values: &[u64]) -> Result<u64> {
values.iter().try_fold(1u64, |acc, &value| {
acc.checked_mul(value)
.ok_or_else(|| Error::InvalidFormat("vector product overflow".into()))
})
}
pub fn vector_zerop_u(values: &[u64]) -> bool {
values.iter().any(|&value| value == 0)
}
pub fn vector_zerop_s(values: &[i64]) -> bool {
values.iter().any(|&value| value == 0)
}
pub fn vector_cmp_u(lhs: &[u64], rhs: &[u64]) -> std::cmp::Ordering {
lhs.cmp(rhs)
}
pub fn vector_cmp_s(lhs: &[i64], rhs: &[i64]) -> std::cmp::Ordering {
lhs.cmp(rhs)
}
pub fn vector_inc(values: &mut [u64], dims: &[u64]) -> bool {
if values.len() != dims.len() {
return false;
}
for index in (0..values.len()).rev() {
values[index] += 1;
if values[index] < dims[index] {
return true;
}
values[index] = 0;
}
false
}
pub fn power2up(value: u64) -> Option<u64> {
if value <= 1 {
Some(1)
} else {
value.checked_next_power_of_two()
}
}
pub fn bit_set(bytes: &mut [u8], bit: usize, value: bool) -> Result<()> {
let byte = bytes
.get_mut(bit / 8)
.ok_or_else(|| Error::InvalidFormat("bit index out of bounds".into()))?;
let mask = 0x80 >> (bit % 8);
if value {
*byte |= mask;
} else {
*byte &= !mask;
}
Ok(())
}
pub fn bit_get(bytes: &[u8], bit: usize) -> Result<bool> {
let byte = bytes
.get(bit / 8)
.ok_or_else(|| Error::InvalidFormat("bit index out of bounds".into()))?;
Ok((*byte & (0x80 >> (bit % 8))) != 0)
}
pub fn log2_of2(value: u64) -> Result<u32> {
if value == 0 || !value.is_power_of_two() {
return Err(Error::InvalidFormat("value is not a power of two".into()));
}
Ok(value.trailing_zeros())
}
pub fn log2_gen(value: u64) -> Result<u32> {
if value == 0 {
return Err(Error::InvalidFormat("log2 input is zero".into()));
}
Ok(u64::BITS - 1 - value.leading_zeros())
}
pub fn stride_optimize1(stride: usize, elem_size: usize) -> Result<usize> {
stride
.checked_mul(elem_size)
.ok_or_else(|| Error::InvalidFormat("stride optimization overflow".into()))
}
pub fn stride_optimize2(stride: usize, count: usize, elem_size: usize) -> Result<usize> {
stride
.checked_mul(count)
.and_then(|value| value.checked_mul(elem_size))
.ok_or_else(|| Error::InvalidFormat("stride optimization overflow".into()))
}
pub fn hyper_stride(start: &[u64], stride: &[u64], index: &[u64]) -> Result<Vec<u64>> {
if start.len() != stride.len() || start.len() != index.len() {
return Err(Error::InvalidFormat("hyperslab rank mismatch".into()));
}
start
.iter()
.zip(stride)
.zip(index)
.map(|((&start, &stride), &index)| {
index
.checked_mul(stride)
.and_then(|delta| start.checked_add(delta))
.ok_or_else(|| Error::InvalidFormat("hyperslab coordinate overflow".into()))
})
.collect()
}
pub fn hyper_eq(start_a: &[u64], stride_a: &[u64], start_b: &[u64], stride_b: &[u64]) -> bool {
start_a == start_b && stride_a == stride_b
}
pub fn hyper_fill<T: Clone>(out: &mut [T], value: T) {
out.fill(value);
}
pub fn hyper_copy<T: Clone>(src: &[T], dst: &mut [T]) -> Result<()> {
if dst.len() < src.len() {
return Err(Error::InvalidFormat(
"hyperslab destination too small".into(),
));
}
dst[..src.len()].clone_from_slice(src);
Ok(())
}
pub fn stride_fill<T: Clone>(
out: &mut [T],
offset: usize,
stride: usize,
count: usize,
value: T,
) -> Result<()> {
let mut pos = offset;
for _ in 0..count {
let slot = out
.get_mut(pos)
.ok_or_else(|| Error::InvalidFormat("stride fill index out of bounds".into()))?;
*slot = value.clone();
pos = pos
.checked_add(stride)
.ok_or_else(|| Error::InvalidFormat("stride fill index overflow".into()))?;
}
Ok(())
}
pub fn stride_copy<T: Clone>(
src: &[T],
dst: &mut [T],
src_stride: usize,
dst_stride: usize,
count: usize,
) -> Result<()> {
stride_copy_s(src, dst, 0, 0, src_stride, dst_stride, count)
}
pub fn stride_copy_s<T: Clone>(
src: &[T],
dst: &mut [T],
mut src_pos: usize,
mut dst_pos: usize,
src_stride: usize,
dst_stride: usize,
count: usize,
) -> Result<()> {
for _ in 0..count {
let value = src
.get(src_pos)
.ok_or_else(|| Error::InvalidFormat("stride copy source out of bounds".into()))?
.clone();
let slot = dst
.get_mut(dst_pos)
.ok_or_else(|| Error::InvalidFormat("stride copy destination out of bounds".into()))?;
*slot = value;
src_pos = src_pos
.checked_add(src_stride)
.ok_or_else(|| Error::InvalidFormat("stride copy source overflow".into()))?;
dst_pos = dst_pos
.checked_add(dst_stride)
.ok_or_else(|| Error::InvalidFormat("stride copy destination overflow".into()))?;
}
Ok(())
}
pub fn array_down(coords: &[u64], dims: &[u64]) -> Result<Vec<u64>> {
if coords.len() != dims.len() {
return Err(Error::InvalidFormat("array rank mismatch".into()));
}
coords
.iter()
.zip(dims)
.map(|(&coord, &dim)| {
if dim == 0 {
Err(Error::InvalidFormat("array dimension is zero".into()))
} else {
Ok(coord % dim)
}
})
.collect()
}
pub fn array_offset_pre(dims: &[u64]) -> Result<Vec<u64>> {
let mut strides = vec![1u64; dims.len()];
if dims.len() > 1 {
for index in (0..dims.len() - 1).rev() {
strides[index] = strides[index + 1]
.checked_mul(dims[index + 1])
.ok_or_else(|| Error::InvalidFormat("array stride overflow".into()))?;
}
}
Ok(strides)
}
pub fn array_offset(coords: &[u64], dims: &[u64]) -> Result<u64> {
if coords.len() != dims.len() {
return Err(Error::InvalidFormat("array rank mismatch".into()));
}
let strides = array_offset_pre(dims)?;
coords
.iter()
.zip(dims)
.zip(strides)
.try_fold(0u64, |acc, ((&coord, &dim), stride)| {
if coord >= dim {
return Err(Error::InvalidFormat(
"array coordinate out of bounds".into(),
));
}
let term = coord
.checked_mul(stride)
.ok_or_else(|| Error::InvalidFormat("array offset overflow".into()))?;
acc.checked_add(term)
.ok_or_else(|| Error::InvalidFormat("array offset overflow".into()))
})
}
pub fn chunk_index(coords: &[u64], dims: &[u64]) -> Result<u64> {
array_offset(coords, dims)
}
pub fn chunk_scaled(coords: &[u64], chunk_dims: &[u64]) -> Result<Vec<u64>> {
if coords.len() != chunk_dims.len() {
return Err(Error::InvalidFormat("chunk rank mismatch".into()));
}
coords
.iter()
.zip(chunk_dims)
.map(|(&coord, &chunk)| {
if chunk == 0 {
Err(Error::InvalidFormat("chunk dimension is zero".into()))
} else {
Ok(coord / chunk)
}
})
.collect()
}
pub fn chunk_index_scaled(coords: &[u64], dims: &[u64], chunk_dims: &[u64]) -> Result<u64> {
let scaled = chunk_scaled(coords, chunk_dims)?;
let scaled_dims = chunk_scaled(dims, chunk_dims)?;
chunk_index(&scaled, &scaled_dims)
}
pub fn opvv<T: Copy, F: Fn(T, T) -> T>(lhs: &[T], rhs: &[T], out: &mut [T], op: F) -> Result<()> {
if lhs.len() != rhs.len() || lhs.len() > out.len() {
return Err(Error::InvalidFormat(
"vector operation length mismatch".into(),
));
}
for ((out, &lhs), &rhs) in out.iter_mut().zip(lhs).zip(rhs) {
*out = op(lhs, rhs);
}
Ok(())
}
pub fn memcpyvv<T: Copy>(
dst: &mut [T],
dst_offsets: &[usize],
src: &[T],
src_offsets: &[usize],
lens: &[usize],
) -> Result<()> {
if dst_offsets.len() != src_offsets.len() || dst_offsets.len() != lens.len() {
return Err(Error::InvalidFormat(
"vector copy segment length mismatch".into(),
));
}
for ((&dst_offset, &src_offset), &len) in dst_offsets.iter().zip(src_offsets).zip(lens) {
let src_end = src_offset
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("vector copy source overflow".into()))?;
let dst_end = dst_offset
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("vector copy destination overflow".into()))?;
let src_window = src
.get(src_offset..src_end)
.ok_or_else(|| Error::InvalidFormat("vector copy source out of bounds".into()))?;
let dst_window = dst
.get_mut(dst_offset..dst_end)
.ok_or_else(|| Error::InvalidFormat("vector copy destination out of bounds".into()))?;
dst_window.copy_from_slice(src_window);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunk_scaling_and_indexing_work() {
assert_eq!(chunk_scaled(&[9, 5], &[4, 2]).unwrap(), vec![2, 2]);
assert_eq!(chunk_index(&[2, 1], &[4, 4]).unwrap(), 9);
}
#[test]
fn vector_copy_segments_work() {
let src = [1, 2, 3, 4, 5];
let mut dst = [0; 5];
memcpyvv(&mut dst, &[0, 3], &src, &[1, 4], &[2, 1]).unwrap();
assert_eq!(dst, [2, 3, 0, 5, 0]);
}
}