use crate::error::{Error, Result};
use crate::shape::Shape;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Strides(Vec<isize>);
impl Strides {
pub fn new(strides: Vec<isize>) -> Self {
Self(strides)
}
pub fn values(&self) -> &[isize] {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Layout {
pub shape: Shape,
pub strides: Strides,
pub offset: usize,
}
impl Layout {
pub fn contiguous(shape: Shape) -> Self {
let strides = contiguous_strides(&shape);
Self {
shape,
strides,
offset: 0,
}
}
pub fn offset_of(&self, index: &[usize]) -> Result<usize> {
let dims = self.shape.dims();
if index.len() != dims.len() {
return Err(Error::RankMismatch {
index_rank: index.len(),
shape_rank: dims.len(),
});
}
for (i, (&ix, &d)) in index.iter().zip(dims).enumerate() {
let _ = i;
if ix >= d {
return Err(Error::IndexOutOfBounds {
index: index.to_vec(),
shape: self.shape.clone(),
});
}
}
let mut off = self.offset as isize;
for (&ix, &s) in index.iter().zip(self.strides.values()) {
off += ix as isize * s;
}
debug_assert!(off >= 0, "negative absolute offset from a valid layout");
Ok(off as usize)
}
pub fn is_contiguous(&self) -> bool {
if self.shape.numel() == 0 {
return true;
}
let expected = contiguous_strides(&self.shape);
self.shape
.dims()
.iter()
.zip(self.strides.values())
.zip(expected.values())
.all(|((&d, &got), &want)| d == 1 || got == want)
}
}
pub fn contiguous_strides(shape: &Shape) -> Strides {
let dims = shape.dims();
let mut strides = vec![0isize; dims.len()];
let mut acc = 1isize;
for (i, &d) in dims.iter().enumerate().rev() {
strides[i] = acc;
acc *= d as isize;
}
Strides(strides)
}