#![allow(dead_code)]
use super::errors::{TensorResult, checked_num_elements, ensure_coordinate_rank};
pub fn flat_index_wrapped(shape: &[usize], coordinate: &[isize]) -> TensorResult<usize> {
ensure_coordinate_rank(shape, coordinate.len())?;
let mut flat = 0usize;
let mut stride = 1usize;
for (&extent, &component) in shape.iter().rev().zip(coordinate.iter().rev()) {
flat += wrap_axis_index(component, extent) * stride;
stride *= extent;
}
Ok(flat)
}
#[inline]
pub fn normalize_flat_index(index: isize, size: usize) -> usize {
debug_assert!(size > 0 && size <= isize::MAX as usize);
index.rem_euclid(size as isize) as usize
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RowMajorLayout {
shape: Vec<usize>,
strides: Vec<usize>,
size: usize,
}
impl RowMajorLayout {
pub fn try_new(shape: &[usize]) -> TensorResult<Self> {
let size = checked_num_elements(shape)?;
let mut strides = vec![1; shape.len()];
for axis in (0..shape.len().saturating_sub(1)).rev() {
strides[axis] = strides[axis + 1] * shape[axis + 1];
}
Ok(Self {
shape: shape.to_vec(),
strides,
size,
})
}
pub fn new(shape: &[usize]) -> Self {
Self::try_new(shape).expect("invalid row-major tensor shape")
}
pub fn shape(&self) -> &[usize] {
&self.shape
}
pub fn strides(&self) -> &[usize] {
&self.strides
}
pub const fn size(&self) -> usize {
self.size
}
pub fn flat_index_wrapped(&self, coordinate: &[isize]) -> TensorResult<usize> {
ensure_coordinate_rank(&self.shape, coordinate.len())?;
Ok(self
.shape
.iter()
.zip(&self.strides)
.zip(coordinate)
.map(|((&extent, &stride), &component)| wrap_axis_index(component, extent) * stride)
.sum())
}
#[inline]
pub fn index_wrapped(&self, coordinate: &[isize]) -> usize {
self.flat_index_wrapped(coordinate)
.expect("tensor index rank mismatch")
}
#[inline]
pub fn flat_index(&self, index: isize) -> usize {
normalize_flat_index(index, self.size)
}
pub fn coordinate(&self, flat: usize) -> Option<Vec<isize>> {
if flat >= self.size {
return None;
}
let mut coordinate = vec![0; self.shape.len()];
self.coordinate_into(flat, &mut coordinate);
Some(coordinate)
}
#[inline]
pub fn coordinate_into(&self, flat: usize, coordinate: &mut [isize]) {
assert!(flat < self.size, "flat tensor index out of bounds");
assert_eq!(
coordinate.len(),
self.shape.len(),
"tensor coordinate rank mismatch"
);
let mut remainder = flat;
for axis in (1..self.shape.len()).rev() {
let extent = self.shape[axis];
coordinate[axis] = (remainder % extent) as isize;
remainder /= extent;
}
coordinate[0] = remainder as isize;
}
}
#[inline]
fn wrap_axis_index(index: isize, extent: usize) -> usize {
debug_assert!(extent > 0);
index.rem_euclid(extent as isize) as usize
}