use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
use crate::domain::dynamic::LayoutDyn;
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::kernels;
use crate::infrastructure::storage::{Storage, StorageMut, VecStorage};
#[derive(Debug, Clone)]
pub struct ArrayD<T, S> {
pub(crate) layout: LayoutDyn,
pub(crate) storage: S,
pub(crate) _marker: PhantomData<T>,
}
impl<T, S> ArrayD<T, S>
where
S: Storage<T>,
{
pub fn new(layout: LayoutDyn, storage: S) -> Result<Self> {
layout.validate_storage_len(storage.len())?;
Ok(Self {
layout,
storage,
_marker: PhantomData,
})
}
#[inline]
pub fn ndim(&self) -> usize {
self.layout.ndim()
}
#[inline]
pub fn shape(&self) -> &[usize] {
&self.layout.shape
}
#[inline]
pub fn strides(&self) -> &[isize] {
&self.layout.strides
}
#[inline]
pub fn offset(&self) -> usize {
self.layout.offset
}
#[inline]
pub fn size(&self) -> usize {
self.layout.size()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.size() == 0
}
#[inline]
pub fn layout(&self) -> &LayoutDyn {
&self.layout
}
#[inline]
pub fn storage(&self) -> &S {
&self.storage
}
#[inline]
pub fn storage_mut(&mut self) -> &mut S {
&mut self.storage
}
#[inline]
pub fn get(&self, index: &[usize]) -> Result<&T> {
let offset = self.layout.offset_of(index)?;
self.storage
.as_slice()
.get(offset)
.ok_or(LetoError::StorageError {
reason: format!(
"physical offset {offset} exceeds backing slice length {}",
self.storage.len()
),
})
}
}
impl<T> ArrayD<T, VecStorage<T>> {
pub fn from_shape_vec(shape: &[usize], data: Vec<T>) -> Result<Self> {
let layout = LayoutDyn::c_contiguous(shape)?;
let size = layout.size();
if data.len() != size {
return Err(LetoError::StorageError {
reason: format!(
"vector length {} does not match layout size {size}",
data.len()
),
});
}
Self::new(layout, VecStorage::new(data))
}
pub fn zeros(shape: &[usize]) -> Result<Self>
where
T: Default + Clone,
{
let layout = LayoutDyn::c_contiguous(shape)?;
let size = layout.size();
Self::new(layout, VecStorage::fill(size, T::default()))
}
pub fn from_elem(shape: &[usize], value: T) -> Result<Self>
where
T: Clone,
{
let layout = LayoutDyn::c_contiguous(shape)?;
let size = layout.size();
Self::new(layout, VecStorage::fill(size, value))
}
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.storage.as_slice().iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
self.storage.as_mut_slice().iter_mut()
}
pub fn to_vec(&self) -> Result<Vec<T>>
where
T: Clone,
{
if self.layout.is_c_contiguous() {
return Ok(self.storage.as_slice()
[self.layout.offset..self.layout.offset + self.size()]
.to_vec());
}
let size = self.size();
let ndim = self.ndim();
let mut out: Vec<T> = Vec::with_capacity(size);
let mut index = vec![0usize; ndim];
for flat in 0..size {
kernels::fill_index_from_flat(flat, &self.layout.shape, &mut index);
out.push(self.get(&index)?.clone());
}
Ok(out)
}
pub fn into_shape(self, shape: &[usize]) -> Result<Self> {
let target = LayoutDyn::c_contiguous(shape)?;
if target.checked_size()? != self.layout.checked_size()? {
return Err(LetoError::ShapeMismatch {
lhs: self.layout.shape.to_vec(),
rhs: shape.to_vec(),
});
}
if !self.layout.is_c_contiguous() {
return Err(LetoError::StorageError {
reason: "into_shape requires a C-contiguous layout".to_string(),
});
}
Ok(Self {
layout: target,
storage: self.storage,
_marker: PhantomData,
})
}
}
impl<T, S> ArrayD<T, S>
where
S: StorageMut<T>,
{
#[inline]
pub fn get_mut(&mut self, index: &[usize]) -> Result<&mut T> {
let offset = self.layout.offset_of(index)?;
let len = self.storage.len();
self.storage
.as_mut_slice()
.get_mut(offset)
.ok_or(LetoError::StorageError {
reason: format!("physical offset {offset} exceeds backing slice length {len}"),
})
}
#[inline]
pub fn set(&mut self, index: &[usize], value: T) -> Result<()> {
*self.get_mut(index)? = value;
Ok(())
}
}
impl<T> ArrayD<T, VecStorage<T>>
where
T: Copy,
{
pub fn fill(&mut self, value: T) {
for slot in self.storage.as_mut_slice() {
*slot = value;
}
}
}
impl<T, S: Storage<T>> Index<&[usize]> for ArrayD<T, S> {
type Output = T;
#[inline]
fn index(&self, index: &[usize]) -> &T {
self.get(index)
.expect("ArrayD index out of bounds or wrong rank")
}
}
impl<T, S: StorageMut<T>> IndexMut<&[usize]> for ArrayD<T, S> {
#[inline]
fn index_mut(&mut self, index: &[usize]) -> &mut T {
self.get_mut(index)
.expect("ArrayD index_mut out of bounds or wrong rank")
}
}