use crate::mxArray;
pub trait Index {
fn index_into(&self, mx: &mxArray) -> Option<usize>;
}
impl Index for usize {
fn index_into(&self, mx: &mxArray) -> Option<usize> {
if *self < mx.numel() {
Some(*self)
} else {
None
}
}
}
fn sub2ind(idx: &[usize], dim: &[usize]) -> Option<usize> {
if dim.len() != idx.len() {
return None;
}
idx.iter().rev()
.zip(dim.iter().rev())
.try_fold((0, 1), |(offset, stride), (idx, dimlen)| {
if idx < dimlen {
let offset = stride*idx + offset;
let stride = stride*dimlen;
Some((offset, stride))
} else {
None
}
})
.map(|(offset, _)| offset)
}
impl Index for &[usize] {
fn index_into(&self, mx: &mxArray) -> Option<usize> {
sub2ind(self, mx.dimensions())
}
}
use core::num::NonZeroUsize;
impl Index for &[NonZeroUsize] {
fn index_into(&self, mx: &mxArray) -> Option<usize> {
let zero_based: Vec<_> = self.iter()
.map(|&one_based_idx| usize::from(one_based_idx) - 1 )
.collect();
sub2ind(&zero_based, mx.dimensions())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sub2ind() {
let arraysize = &[3, 3];
assert_eq!(sub2ind(&[0, 0], arraysize), Some(0));
assert_eq!(sub2ind(&[0, 1], arraysize), Some(1));
assert_eq!(sub2ind(&[0, 2], arraysize), Some(2));
assert_eq!(sub2ind(&[0, 3], arraysize), None);
assert_eq!(sub2ind(&[1, 0], arraysize), Some(3));
assert_eq!(sub2ind(&[1, 1], arraysize), Some(4));
assert_eq!(sub2ind(&[1, 2], arraysize), Some(5));
assert_eq!(sub2ind(&[1, 3], arraysize), None);
assert_eq!(sub2ind(&[2, 0], arraysize), Some(6));
assert_eq!(sub2ind(&[2, 1], arraysize), Some(7));
assert_eq!(sub2ind(&[2, 2], arraysize), Some(8));
assert_eq!(sub2ind(&[2, 3], arraysize), None);
assert_eq!(sub2ind(&[3, 3], arraysize), None);
}
#[test]
fn test_sub2ind_3d() {
let arraysize = &[2, 2, 2];
assert_eq!(sub2ind(&[0, 0, 0], arraysize), Some(0));
assert_eq!(sub2ind(&[1, 0, 0], arraysize), Some(4));
}
}