use core::fmt;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ViewError {
InvalidShape {
message: &'static str,
},
OutOfBounds {
index: usize,
len: usize,
},
}
impl fmt::Display for ViewError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidShape { message } => write!(f, "invalid view shape: {message}"),
Self::OutOfBounds { index, len } => {
write!(f, "index {index} out of bounds for length {len}")
}
}
}
}
impl std::error::Error for ViewError {}
#[derive(Clone, Copy, Debug)]
pub struct F64VectorView<'a> {
data: &'a [f64],
len: usize,
stride: usize,
}
impl<'a> F64VectorView<'a> {
#[must_use]
pub const fn contiguous(data: &'a [f64]) -> Self {
Self { data, len: data.len(), stride: 1 }
}
pub fn strided(data: &'a [f64], len: usize, stride: usize) -> Result<Self, ViewError> {
if len == 0 {
return Ok(Self { data, len: 0, stride: stride.max(1) });
}
if stride == 0 {
return Err(ViewError::InvalidShape { message: "stride must be non-zero" });
}
let need = (len - 1)
.checked_mul(stride)
.and_then(|v| v.checked_add(1))
.ok_or(ViewError::InvalidShape { message: "stride*len overflow" })?;
if data.len() < need {
return Err(ViewError::InvalidShape {
message: "underlying slice shorter than strided extent",
});
}
Ok(Self { data, len, stride })
}
#[must_use]
pub const fn len(self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.len == 0
}
#[must_use]
pub const fn stride(self) -> usize {
self.stride
}
#[must_use]
pub const fn is_contiguous(self) -> bool {
self.stride == 1
}
pub fn get(self, i: usize) -> Result<f64, ViewError> {
if i >= self.len {
return Err(ViewError::OutOfBounds { index: i, len: self.len });
}
Ok(self.data[i * self.stride])
}
#[inline]
#[must_use]
pub unsafe fn get_unchecked(self, i: usize) -> f64 {
unsafe { *self.data.get_unchecked(i * self.stride) }
}
#[must_use]
pub fn as_slice(self) -> Option<&'a [f64]> {
if self.is_contiguous() { Some(&self.data[..self.len]) } else { None }
}
}
#[derive(Clone, Copy, Debug)]
pub struct F64MatrixView<'a> {
data: &'a [f64],
nrows: usize,
ncols: usize,
row_stride: usize,
col_stride: usize,
}
impl<'a> F64MatrixView<'a> {
pub fn column_major(data: &'a [f64], nrows: usize, ncols: usize) -> Result<Self, ViewError> {
let need = nrows
.checked_mul(ncols)
.ok_or(ViewError::InvalidShape { message: "nrows*ncols overflow" })?;
if data.len() < need {
return Err(ViewError::InvalidShape { message: "buffer shorter than matrix" });
}
Ok(Self { data, nrows, ncols, row_stride: 1, col_stride: nrows })
}
#[must_use]
pub const fn nrows(self) -> usize {
self.nrows
}
#[must_use]
pub const fn ncols(self) -> usize {
self.ncols
}
pub fn get(self, row: usize, col: usize) -> Result<f64, ViewError> {
if row >= self.nrows {
return Err(ViewError::OutOfBounds { index: row, len: self.nrows });
}
if col >= self.ncols {
return Err(ViewError::OutOfBounds { index: col, len: self.ncols });
}
Ok(self.data[row * self.row_stride + col * self.col_stride])
}
pub fn column(self, j: usize) -> Result<F64VectorView<'a>, ViewError> {
if j >= self.ncols {
return Err(ViewError::OutOfBounds { index: j, len: self.ncols });
}
let offset = j * self.col_stride;
F64VectorView::strided(&self.data[offset..], self.nrows, self.row_stride)
}
}
#[derive(Clone, Copy, Debug)]
pub struct BitMaskView<'a> {
bytes: &'a [u8],
len: usize,
}
impl<'a> BitMaskView<'a> {
pub fn new(bytes: &'a [u8], len: usize) -> Result<Self, ViewError> {
let need = len.div_ceil(8);
if bytes.len() < need {
return Err(ViewError::InvalidShape { message: "mask buffer too short" });
}
Ok(Self { bytes, len })
}
#[must_use]
pub const fn len(self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.len == 0
}
#[must_use]
pub fn get(self, i: usize) -> bool {
if i >= self.len {
return false;
}
let byte = self.bytes[i / 8];
(byte >> (i % 8)) & 1 == 1
}
}