use crate::*;
use std::ops::{Index, IndexMut};
#[derive(Debug, PartialEq)]
pub struct RArray<T, D> {
data: T,
dim: D,
}
pub type RColumn<T> = RArray<T, [usize; 1]>;
pub type RMatrix<T> = RArray<T, [usize; 2]>;
pub type RMatrix3D<T> = RArray<T, [usize; 3]>;
const BASE: usize = 0;
trait Offset<D> {
fn offset(&self, idx: D) -> usize;
}
impl<T> Offset<[usize; 1]> for RArray<T, [usize; 1]> {
fn offset(&self, index: [usize; 1]) -> usize {
if index[0] - BASE > self.dim[0] {
panic!("array index: row overflow");
}
index[0] - BASE
}
}
impl<T> Offset<[usize; 2]> for RArray<T, [usize; 2]> {
fn offset(&self, index: [usize; 2]) -> usize {
if index[0] - BASE > self.dim[0] {
panic!("matrix index: row overflow");
}
if index[1] - BASE > self.dim[1] {
panic!("matrix index: column overflow");
}
(index[0] - BASE) + self.dim[0] * (index[1] - BASE)
}
}
impl<T> Offset<[usize; 3]> for RArray<T, [usize; 3]> {
fn offset(&self, index: [usize; 3]) -> usize {
if index[0] - BASE > self.dim[0] {
panic!("RMatrix3D index: row overflow");
}
if index[1] - BASE > self.dim[1] {
panic!("RMatrix3D index: column overflow");
}
if index[2] - BASE > self.dim[2] {
panic!("RMatrix3D index: submatrix overflow");
}
(index[0] - BASE) + self.dim[0] * (index[1] - BASE + self.dim[1] * (index[2] - BASE))
}
}
impl<T, D> RArray<T, D> {
pub fn data(&self) -> &T {
&self.data
}
pub fn dim(&self) -> &D {
&self.dim
}
}
impl<T> RColumn<T> {
pub fn new(data: T, nrows: usize) -> Self {
let dim = [nrows];
Self { data, dim }
}
pub fn nrows(&self) -> usize {
self.dim[0]
}
}
impl<T> RMatrix<T> {
pub fn new(data: T, nrows: usize, ncols: usize) -> Self {
let dim = [nrows, ncols];
Self { data, dim }
}
pub fn nrows(&self) -> usize {
self.dim[0]
}
pub fn ncols(&self) -> usize {
self.dim[1]
}
}
impl<T> RMatrix3D<T> {
pub fn new(data: T, nrows: usize, ncols: usize, nsub: usize) -> Self {
let dim = [nrows, ncols, nsub];
Self { data, dim }
}
pub fn nrows(&self) -> usize {
self.dim[0]
}
pub fn ncols(&self) -> usize {
self.dim[1]
}
pub fn nsub(&self) -> usize {
self.dim[2]
}
}
impl<T> From<RColumn<T>> for Robj
where
T: Into<Robj>,
{
fn from(array: RColumn<T>) -> Self {
let res = array.data.into();
res
}
}
impl<T> From<RArray<T, [usize; 2]>> for Robj
where
T: Into<Robj>,
{
fn from(array: RArray<T, [usize; 2]>) -> Self {
let res = array.data.into();
let dim = [array.dim[0] as i32, array.dim[1] as i32];
res.set_attrib(dim_symbol(), dim)
.expect("From<Matrix> failed")
.set_attrib(class_symbol(), "matrix")
.expect("From<Matrix> failed")
}
}
impl<T> From<RArray<T, [usize; 3]>> for Robj
where
T: Into<Robj>,
{
fn from(array: RArray<T, [usize; 3]>) -> Self {
let res = array.data.into();
let dim = [
array.dim[0] as i32,
array.dim[1] as i32,
array.dim[2] as i32,
];
res.set_attrib(dim_symbol(), dim)
.expect("From<Matrix3D> failed")
.set_attrib(class_symbol(), "array")
.expect("From<Matrix3D> failed")
}
}
impl Robj {
pub fn as_vector<'a, E>(&self) -> Option<RColumn<&'a [E]>>
where
Self: AsTypedSlice<'a, E>,
{
if let Some(data) = self.as_typed_slice() {
let dim = [self.nrows() as usize];
return Some(RArray { data, dim });
}
None
}
pub fn as_matrix<'a, E>(&self) -> Option<RMatrix<&'a [E]>>
where
Self: AsTypedSlice<'a, E>,
{
if self.is_matrix() {
if let Some(data) = self.as_typed_slice() {
let dim = [self.nrows() as usize, self.ncols() as usize];
return Some(RArray { data, dim });
}
}
None
}
pub fn as_matrix3d<'a, E>(&self) -> Option<RMatrix3D<&'a [E]>>
where
Self: AsTypedSlice<'a, E>,
{
if self.is_array() {
if let Some(data) = self.as_typed_slice() {
if let Some(dim) = self.dim() {
let dim: Vec<_> = dim.collect();
let dim = [dim[0] as usize, dim[1] as usize, dim[2] as usize];
return Some(RArray { data, dim });
}
}
}
None
}
}
impl<T> Index<[usize; 2]> for RArray<T, [usize; 2]>
where
T: Index<usize>,
{
type Output = <T as Index<usize>>::Output;
fn index(&self, index: [usize; 2]) -> &Self::Output {
&self.data[self.offset(index)]
}
}
impl<T> IndexMut<[usize; 2]> for RArray<T, [usize; 2]>
where
T: IndexMut<usize>,
{
fn index_mut(&mut self, index: [usize; 2]) -> &mut Self::Output {
let offset = self.offset(index);
&mut self.data[offset]
}
}
#[test]
fn matrix_ops() {
test! {
let vector = RColumn::new([1., 2., 3.], 3);
let robj = r!(vector);
assert_eq!(robj.is_vector(), true);
assert_eq!(robj.nrows(), 3);
let vector2 : RColumn<&[f64]> = robj.as_vector().ok_or("expected array")?;
assert_eq!(vector2.data().len(), 3);
assert_eq!(vector2.nrows(), 3);
let matrix = RMatrix::new([
1., 2., 3.,
4., 5., 6.], 3, 2);
let robj = r!(matrix);
assert_eq!(robj.is_matrix(), true);
assert_eq!(robj.nrows(), 3);
assert_eq!(robj.ncols(), 2);
let matrix2 : RMatrix<&[f64]> = robj.as_matrix().ok_or("expected matrix")?;
assert_eq!(matrix2.data().len(), 6);
assert_eq!(matrix2.nrows(), 3);
assert_eq!(matrix2.ncols(), 2);
let array = RMatrix3D::new([
1., 2., 3., 4.,
5., 6., 7., 8.], 2, 2, 2);
let robj = r!(array);
assert_eq!(robj.is_array(), true);
assert_eq!(robj.nrows(), 2);
assert_eq!(robj.ncols(), 2);
let array2 : RMatrix3D<&[f64]> = robj.as_matrix3d().ok_or("expected matrix3d")?;
assert_eq!(array2.data().len(), 8);
assert_eq!(array2.nrows(), 2);
assert_eq!(array2.ncols(), 2);
assert_eq!(array2.nsub(), 2);
}
}