rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Array indexing functionality
 */
use crate::mxArray;

/**
 * Trait to generalize over the several ways to index into a Matlab array
 */
pub trait Index {
	fn index_into(&self, mx: &mxArray) -> Option<usize>;
}

/// Implementation for a linear index.
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)
}

/// Implementation for subscripting indices. Assumes 0-based arrays
impl Index for &[usize] {
	fn index_into(&self, mx: &mxArray) -> Option<usize> {
		sub2ind(self, mx.dimensions())
	}
}

use core::num::NonZeroUsize;
/// Implementation for subscripting indices. Assumes 1-based arrays.
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())
	}
}

// TODO: this test cannot yet be run, for as long as the bindings to the MEX api's are
// still integrated within this crate. It would be better if the crates which handle the
// bindings are decoupled from rustmex.
#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn test_sub2ind() {
		let arraysize = &[3, 3];
				// row, column
		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];
				// plane, row, column
		assert_eq!(sub2ind(&[0, 0, 0], arraysize), Some(0));
		assert_eq!(sub2ind(&[1, 0, 0], arraysize), Some(4));
	}
}