extendr_api/wrapper/
matrix.rs

1//! Wrappers for matrices with deferred arithmetic.
2use self::robj::{AsTypedSlice, Robj};
3use super::*;
4use crate::scalar::Scalar;
5use extendr_ffi::{
6    Rf_GetArrayDimnames, Rf_GetColNames, Rf_GetRowNames, Rf_dimgets, Rf_dimnamesgets, Rf_namesgets,
7    TYPEOF,
8};
9use std::ops::{Index, IndexMut};
10
11/// Wrapper for creating and using matrices and arrays.
12///
13/// ```
14/// use extendr_api::prelude::*;
15/// test! {
16///     let matrix = RMatrix::new_matrix(3, 2, |r, c| [
17///         [1., 2., 3.],
18///          [4., 5., 6.]][c][r]);
19///     let robj = r!(matrix);
20///     assert_eq!(robj.is_matrix(), true);
21///     assert_eq!(robj.nrows(), 3);
22///     assert_eq!(robj.ncols(), 2);
23///
24///     let matrix2 : RMatrix<f64> = robj.as_matrix().ok_or("error")?;
25///     assert_eq!(matrix2.data().len(), 6);
26///     assert_eq!(matrix2.nrows(), 3);
27///     assert_eq!(matrix2.ncols(), 2);
28/// }
29/// ```
30#[derive(Debug, PartialEq)]
31pub struct RArray<T, D> {
32    /// Owning Robj (probably should be a Pin).
33    robj: Robj,
34
35    /// Dimensions of the array.
36    dim: D,
37
38    _data: std::marker::PhantomData<T>,
39}
40
41impl<T, D> RArray<T, D> {
42    pub fn get_dimnames(&self) -> List {
43        List::try_from(Robj::from_sexp(unsafe { Rf_GetArrayDimnames(self.get()) })).unwrap()
44    }
45
46    /// Set the names of the elements of an array.
47    ///
48    ///
49    /// Equivalent to `names<-` in R
50    pub fn set_names(&mut self, names: Strings) {
51        // TODO: check what `names` are and validate the input...
52        let _ = unsafe { Rf_namesgets(self.get_mut(), names.get()) };
53    }
54
55    /// Set the dimension names of an array.
56    ///
57    /// For [`RMatrix`] a list of length 2 is required, as that would entail
58    /// column-names and row-names. If you only wish to set one, but not the other,
59    /// then the unset element must be R `NULL`
60    ///
61    /// Equivalent to `dimnames<-` in R
62    pub fn set_dimnames(&mut self, dimnames: List) {
63        let _ = unsafe { Rf_dimnamesgets(self.get_mut(), dimnames.get()) };
64    }
65
66    /// Set the dimensions of an array.
67    ///
68    /// Equivalent to `dim<-`
69    pub fn set_dim(&mut self, dim: Robj) {
70        // TODO: ensure that Robj is LGLSXP, INTSXP, REALSXP, CPLXSXP, STRSXP, RAWSXP
71        // or NilValue
72        let _ = unsafe { Rf_dimgets(self.get_mut(), dim.get()) };
73    }
74}
75
76pub type RColumn<T> = RArray<T, [usize; 1]>;
77pub type RMatrix<T> = RArray<T, [usize; 2]>;
78pub type RMatrix3D<T> = RArray<T, [usize; 3]>;
79pub type RMatrix4D<T> = RArray<T, [usize; 4]>;
80pub type RMatrix5D<T> = RArray<T, [usize; 5]>;
81
82impl<T> RMatrix<T>
83where
84    T: ToVectorValue,
85    Robj: for<'a> AsTypedSlice<'a, T>,
86{
87    /// Returns an [`RMatrix`] with dimensions according to `nrow` and `ncol`,
88    /// with arbitrary entries. To initialize a matrix containing only `NA`
89    /// values, use [`RMatrix::new_with_na`].
90    pub fn new(nrow: usize, ncol: usize) -> Self {
91        let sexptype = T::sexptype();
92        let matrix = Robj::alloc_matrix(sexptype, nrow as _, ncol as _);
93        RArray::from_parts(matrix, [nrow, ncol])
94    }
95}
96
97impl<T> RMatrix<T>
98where
99    T: ToVectorValue + CanBeNA,
100    Robj: for<'a> AsTypedSlice<'a, T>,
101{
102    /// Returns an [`RMatrix`] with dimensions according to `nrow` and `ncol`,
103    /// with all entries set to `NA`.
104    ///
105    /// Note that since [`Raw`] does not have an NA representation in R,
106    /// this method is not implemented for [`Rbyte`].
107    pub fn new_with_na(nrow: usize, ncol: usize) -> Self {
108        let mut matrix = Self::new(nrow, ncol);
109        if nrow != 0 || ncol != 0 {
110            matrix
111                .as_typed_slice_mut()
112                .unwrap()
113                .iter_mut()
114                .for_each(|x| {
115                    *x = T::na();
116                });
117        }
118        matrix
119    }
120}
121
122impl<T> RMatrix<T> {
123    pub fn get_colnames(&self) -> Option<Strings> {
124        unsafe {
125            let maybe_colnames = Rf_GetColNames(Rf_GetArrayDimnames(self.get()));
126            match TYPEOF(maybe_colnames) {
127                SEXPTYPE::NILSXP => None,
128                SEXPTYPE::STRSXP => {
129                    let colnames = Robj::from_sexp(maybe_colnames);
130                    Some(std::mem::transmute(colnames))
131                }
132                _ => unreachable!(
133                    "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
134                ),
135            }
136        }
137    }
138    pub fn get_rownames(&self) -> Option<Strings> {
139        unsafe {
140            let maybe_rownames = Rf_GetRowNames(Rf_GetArrayDimnames(self.get()));
141            match TYPEOF(maybe_rownames) {
142                SEXPTYPE::NILSXP => None,
143                SEXPTYPE::STRSXP => {
144                    let rownames = Robj::from_sexp(maybe_rownames);
145                    Some(std::mem::transmute(rownames))
146                }
147                _ => unreachable!(
148                    "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
149                ),
150            }
151        }
152    }
153}
154
155const BASE: usize = 0;
156
157trait Offset<D> {
158    /// Get the offset into the array for a given index.
159    fn offset(&self, idx: D) -> usize;
160}
161
162impl<T> Offset<[usize; 1]> for RArray<T, [usize; 1]> {
163    /// Get the offset into the array for a given index.
164    fn offset(&self, index: [usize; 1]) -> usize {
165        if index[0] - BASE > self.dim[0] {
166            panic!("array index: row overflow");
167        }
168        index[0] - BASE
169    }
170}
171
172impl<T> Offset<[usize; 2]> for RArray<T, [usize; 2]> {
173    /// Get the offset into the array for a given index.
174    fn offset(&self, index: [usize; 2]) -> usize {
175        if index[0] - BASE > self.dim[0] {
176            panic!("matrix index: row overflow");
177        }
178        if index[1] - BASE > self.dim[1] {
179            panic!("matrix index: column overflow");
180        }
181        (index[0] - BASE) + self.dim[0] * (index[1] - BASE)
182    }
183}
184
185impl<T> Offset<[usize; 3]> for RArray<T, [usize; 3]> {
186    /// Get the offset into the array for a given index.
187    fn offset(&self, index: [usize; 3]) -> usize {
188        if index[0] - BASE > self.dim[0] {
189            panic!("RMatrix3D index: row overflow");
190        }
191        if index[1] - BASE > self.dim[1] {
192            panic!("RMatrix3D index: column overflow");
193        }
194        if index[2] - BASE > self.dim[2] {
195            panic!("RMatrix3D index: submatrix overflow");
196        }
197        (index[0] - BASE) + self.dim[0] * (index[1] - BASE + self.dim[1] * (index[2] - BASE))
198    }
199}
200
201impl<T, D> RArray<T, D>
202where
203    Robj: for<'a> AsTypedSlice<'a, T>,
204{
205    pub fn from_parts(robj: Robj, dim: D) -> Self {
206        Self {
207            robj,
208            dim,
209            _data: std::marker::PhantomData,
210        }
211    }
212
213    /// Returns a flat representation of the array in col-major.
214    pub fn data(&self) -> &[T] {
215        self.as_typed_slice().unwrap()
216    }
217
218    /// Returns a flat, mutable representation of the array in col-major.
219    pub fn data_mut(&mut self) -> &mut [T] {
220        self.as_typed_slice_mut().unwrap()
221    }
222
223    /// Get the dimensions for this array.
224    pub fn dim(&self) -> &D {
225        &self.dim
226    }
227}
228
229impl<T> RColumn<T>
230where
231    T: ToVectorValue,
232    Robj: for<'a> AsTypedSlice<'a, T>,
233{
234    /// Make a new column type.
235    pub fn new_column<F: FnMut(usize) -> T>(nrows: usize, f: F) -> Self {
236        let mut robj = (0..nrows).map(f).collect_robj();
237        let dim = [nrows];
238        robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
239        RArray::from_parts(robj, dim)
240    }
241
242    /// Get the number of rows.
243    pub fn nrows(&self) -> usize {
244        self.dim[0]
245    }
246}
247
248impl<T> RMatrix<T>
249where
250    T: ToVectorValue,
251    Robj: for<'a> AsTypedSlice<'a, T>,
252{
253    /// Create a new matrix wrapper.
254    ///
255    /// # Arguments
256    ///
257    /// * `nrows` - the number of rows the returned matrix will have
258    /// * `ncols` - the number of columns the returned matrix will have
259    /// * `f` - a function that will be called for each entry of the matrix in order to populate it with values.
260    ///     It must return a scalar value that can be converted to an R scalar, such as `i32`, `u32`, or `f64`, i.e. see [ToVectorValue].
261    ///     It accepts two arguments:
262    ///     * `r` - the current row of the entry we are creating
263    ///     * `c` - the current column of the entry we are creating
264    pub fn new_matrix<F: Clone + FnMut(usize, usize) -> T>(
265        nrows: usize,
266        ncols: usize,
267        f: F,
268    ) -> Self {
269        let mut robj = (0..ncols)
270            .flat_map(|c| {
271                let mut g = f.clone();
272                (0..nrows).map(move |r| g(r, c))
273            })
274            .collect_robj();
275        let dim = [nrows, ncols];
276        robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
277        RArray::from_parts(robj, dim)
278    }
279
280    /// Get the number of rows.
281    pub fn nrows(&self) -> usize {
282        self.dim[0]
283    }
284
285    /// Get the number of columns.
286    pub fn ncols(&self) -> usize {
287        self.dim[1]
288    }
289}
290
291impl<T> RMatrix3D<T>
292where
293    T: ToVectorValue,
294    Robj: for<'a> AsTypedSlice<'a, T>,
295{
296    pub fn new_matrix3d<F: Clone + FnMut(usize, usize, usize) -> T>(
297        nrows: usize,
298        ncols: usize,
299        nmatrix: usize,
300        f: F,
301    ) -> Self {
302        let mut robj = (0..nmatrix)
303            .flat_map(|m| {
304                let h = f.clone();
305                (0..ncols).flat_map(move |c| {
306                    let mut g = h.clone();
307                    (0..nrows).map(move |r| g(r, c, m))
308                })
309            })
310            .collect_robj();
311        let dim = [nrows, ncols, nmatrix];
312        robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
313        RArray::from_parts(robj, dim)
314    }
315
316    /// Get the number of rows.
317    pub fn nrows(&self) -> usize {
318        self.dim[0]
319    }
320
321    /// Get the number of columns.
322    pub fn ncols(&self) -> usize {
323        self.dim[1]
324    }
325
326    /// Get the number of submatrices.
327    pub fn nsub(&self) -> usize {
328        self.dim[2]
329    }
330}
331
332impl<T> TryFrom<&Robj> for RColumn<T>
333where
334    Robj: for<'a> AsTypedSlice<'a, T>,
335{
336    type Error = Error;
337
338    fn try_from(robj: &Robj) -> Result<Self> {
339        if let Some(_slice) = robj.as_typed_slice() {
340            let len = robj.len();
341            Ok(RArray::from_parts(robj.clone(), [len]))
342        } else {
343            Err(Error::ExpectedVector(robj.clone()))
344        }
345    }
346}
347
348impl<T> TryFrom<&Robj> for RMatrix<T>
349where
350    Robj: for<'a> AsTypedSlice<'a, T>,
351{
352    type Error = Error;
353
354    fn try_from(robj: &Robj) -> Result<Self> {
355        if !robj.is_matrix() {
356            Err(Error::ExpectedMatrix(robj.clone()))
357        } else if let Some(_slice) = robj.as_typed_slice() {
358            if let Some(dim) = robj.dim() {
359                let dim: Vec<_> = dim.iter().map(|d| d.inner() as usize).collect();
360                if dim.len() != 2 {
361                    Err(Error::ExpectedMatrix(robj.clone()))
362                } else {
363                    Ok(RArray::from_parts(robj.clone(), [dim[0], dim[1]]))
364                }
365            } else {
366                Err(Error::ExpectedMatrix(robj.clone()))
367            }
368        } else {
369            Err(Error::TypeMismatch(robj.clone()))
370        }
371    }
372}
373
374impl<T> TryFrom<&Robj> for RMatrix3D<T>
375where
376    Robj: for<'a> AsTypedSlice<'a, T>,
377{
378    type Error = Error;
379
380    fn try_from(robj: &Robj) -> Result<Self> {
381        if let Some(_slice) = robj.as_typed_slice() {
382            if let Some(dim) = robj.dim() {
383                if dim.len() != 3 {
384                    Err(Error::ExpectedMatrix3D(robj.clone()))
385                } else {
386                    let dim: Vec<_> = dim.iter().map(|d| d.inner() as usize).collect();
387                    Ok(RArray::from_parts(robj.clone(), [dim[0], dim[1], dim[2]]))
388                }
389            } else {
390                Err(Error::ExpectedMatrix3D(robj.clone()))
391            }
392        } else {
393            Err(Error::TypeMismatch(robj.clone()))
394        }
395    }
396}
397
398impl<T> TryFrom<&Robj> for RMatrix4D<T>
399where
400    Robj: for<'a> AsTypedSlice<'a, T>,
401{
402    type Error = Error;
403
404    fn try_from(robj: &Robj) -> Result<Self> {
405        if let Some(_slice) = robj.as_typed_slice() {
406            if let Some(dim) = robj.dim() {
407                if dim.len() != 4 {
408                    Err(Error::ExpectedMatrix4D(robj.clone()))
409                } else {
410                    let dim: Vec<_> = dim.iter().map(|d| d.inner() as usize).collect();
411                    Ok(RArray::from_parts(
412                        robj.clone(),
413                        [dim[0], dim[1], dim[2], dim[3]],
414                    ))
415                }
416            } else {
417                Err(Error::ExpectedMatrix4D(robj.clone()))
418            }
419        } else {
420            Err(Error::TypeMismatch(robj.clone()))
421        }
422    }
423}
424
425impl<T> TryFrom<&Robj> for RMatrix5D<T>
426where
427    Robj: for<'a> AsTypedSlice<'a, T>,
428{
429    type Error = Error;
430
431    fn try_from(robj: &Robj) -> Result<Self> {
432        if let Some(_slice) = robj.as_typed_slice() {
433            if let Some(dim) = robj.dim() {
434                if dim.len() != 5 {
435                    Err(Error::ExpectedMatrix5D(robj.clone()))
436                } else {
437                    let dim: Vec<_> = dim.iter().map(|d| d.inner() as usize).collect();
438                    Ok(RArray::from_parts(
439                        robj.clone(),
440                        [dim[0], dim[1], dim[2], dim[3], dim[4]],
441                    ))
442                }
443            } else {
444                Err(Error::ExpectedMatrix5D(robj.clone()))
445            }
446        } else {
447            Err(Error::TypeMismatch(robj.clone()))
448        }
449    }
450}
451
452macro_rules! impl_try_from_robj_ref {
453    ($($type : tt)*) => {
454        $(
455            impl<T> TryFrom<Robj> for $type<T>
456            where
457                Robj: for<'a> AsTypedSlice<'a, T>,
458            {
459                type Error = Error;
460
461                fn try_from(robj: Robj) -> Result<Self> {
462                    <$type<T>>::try_from(&robj)
463                }
464            }
465
466            impl<T> TryFrom<&Robj> for Option<$type<T>>
467            where
468                Robj: for<'a> AsTypedSlice<'a, T>,
469            {
470                type Error = Error;
471
472                fn try_from(robj: &Robj) -> Result<Self> {
473                    if robj.is_null() || robj.is_na() {
474                        Ok(None)
475                    } else {
476                        Ok(Some(<$type<T>>::try_from(robj)?))
477                    }
478                }
479            }
480
481            impl<T> TryFrom<Robj> for Option<$type<T>>
482            where
483                Robj: for<'a> AsTypedSlice<'a, T>,
484            {
485                type Error = Error;
486
487                fn try_from(robj: Robj) -> Result<Self> {
488                    <Option::<$type<T>>>::try_from(&robj)
489                }
490            }
491        )*
492    }
493}
494
495impl_try_from_robj_ref!(
496    RMatrix
497    RColumn
498    RMatrix3D
499    RMatrix4D
500    RMatrix5D
501);
502
503impl<T, D> From<RArray<T, D>> for Robj {
504    /// Convert a column, matrix or matrix3d to an Robj.
505    fn from(array: RArray<T, D>) -> Self {
506        array.robj
507    }
508}
509
510pub trait MatrixConversions: GetSexp {
511    fn as_column<E>(&self) -> Option<RColumn<E>>
512    where
513        Robj: for<'a> AsTypedSlice<'a, E>,
514    {
515        <RColumn<E>>::try_from(self.as_robj()).ok()
516    }
517
518    fn as_matrix<E>(&self) -> Option<RMatrix<E>>
519    where
520        Robj: for<'a> AsTypedSlice<'a, E>,
521    {
522        <RMatrix<E>>::try_from(self.as_robj()).ok()
523    }
524
525    fn as_matrix3d<E>(&self) -> Option<RMatrix3D<E>>
526    where
527        Robj: for<'a> AsTypedSlice<'a, E>,
528    {
529        <RMatrix3D<E>>::try_from(self.as_robj()).ok()
530    }
531}
532
533impl MatrixConversions for Robj {}
534
535impl<T> Index<[usize; 2]> for RArray<T, [usize; 2]>
536where
537    Robj: for<'a> AsTypedSlice<'a, T>,
538{
539    type Output = T;
540
541    /// Zero-based indexing in row, column order.
542    ///
543    /// Panics if out of bounds.
544    /// ```
545    /// use extendr_api::prelude::*;
546    /// test! {
547    ///    let matrix = RArray::new_matrix(3, 2, |r, c| [
548    ///        [1., 2., 3.],
549    ///        [4., 5., 6.]][c][r]);
550    ///     assert_eq!(matrix[[0, 0]], 1.);
551    ///     assert_eq!(matrix[[1, 0]], 2.);
552    ///     assert_eq!(matrix[[2, 1]], 6.);
553    /// }
554    /// ```
555    fn index(&self, index: [usize; 2]) -> &Self::Output {
556        unsafe {
557            self.data()
558                .as_ptr()
559                .add(self.offset(index))
560                .as_ref()
561                .unwrap()
562        }
563    }
564}
565
566impl<T> IndexMut<[usize; 2]> for RArray<T, [usize; 2]>
567where
568    Robj: for<'a> AsTypedSlice<'a, T>,
569{
570    /// Zero-based mutable indexing in row, column order.
571    ///
572    /// Panics if out of bounds.
573    /// ```
574    /// use extendr_api::prelude::*;
575    /// test! {
576    ///     let mut matrix = RMatrix::new_matrix(3, 2, |_, _| 0.);
577    ///     matrix[[0, 0]] = 1.;
578    ///     matrix[[1, 0]] = 2.;
579    ///     matrix[[2, 0]] = 3.;
580    ///     matrix[[0, 1]] = 4.;
581    ///     assert_eq!(matrix.as_real_slice().unwrap(), &[1., 2., 3., 4., 0., 0.]);
582    /// }
583    /// ```
584    fn index_mut(&mut self, index: [usize; 2]) -> &mut Self::Output {
585        unsafe {
586            self.data_mut()
587                .as_mut_ptr()
588                .add(self.offset(index))
589                .as_mut()
590                .unwrap()
591        }
592    }
593}
594
595impl<T> Index<[usize; 3]> for RArray<T, [usize; 3]>
596where
597    Robj: for<'a> AsTypedSlice<'a, T>,
598{
599    type Output = T;
600
601    /// Zero-based indexing in row, column order.
602    ///
603    /// Panics if out of bounds.
604    /// ```
605    /// use extendr_api::prelude::*;
606    /// test! {
607    ///    let matrix = RArray::new_matrix3d(3, 2, 2, |r, c, d| (r + c + d) as f64);
608    ///     assert_eq!(matrix[[0, 0, 0]], 0.);
609    ///     assert_eq!(matrix[[1, 0, 1]], 2.);
610    ///     assert_eq!(matrix[[2, 1, 1]], 4.);
611    /// }
612    /// ```
613    fn index(&self, index: [usize; 3]) -> &Self::Output {
614        unsafe {
615            self.data()
616                .as_ptr()
617                .add(self.offset(index))
618                .as_ref()
619                .unwrap()
620        }
621    }
622}
623
624impl<T> IndexMut<[usize; 3]> for RArray<T, [usize; 3]>
625where
626    Robj: for<'a> AsTypedSlice<'a, T>,
627{
628    /// Zero-based mutable indexing in row, column order.
629    ///
630    /// Panics if out of bounds.
631    /// ```
632    /// use extendr_api::prelude::*;
633    /// test! {
634    ///    let mut matrix = RMatrix3D::new_matrix3d(3, 2, 2, |_, _, _| 0.);
635    ///    matrix[[0, 0, 0]] = 1.;
636    ///    matrix[[1, 0, 0]] = 2.;
637    ///    matrix[[2, 0, 0]] = 3.;
638    ///    matrix[[0, 1, 0]] = 4.;
639    ///    assert_eq!(matrix.as_real_slice().unwrap(),
640    ///        &[1., 2., 3., 4., 0., 0., 0., 0., 0., 0., 0., 0.]);
641    /// }
642    /// ```
643    fn index_mut(&mut self, index: [usize; 3]) -> &mut Self::Output {
644        unsafe {
645            self.data_mut()
646                .as_mut_ptr()
647                .add(self.offset(index))
648                .as_mut()
649                .unwrap()
650        }
651    }
652}
653
654impl<T, D> Deref for RArray<T, D> {
655    type Target = Robj;
656
657    fn deref(&self) -> &Self::Target {
658        &self.robj
659    }
660}
661
662impl<T, D> DerefMut for RArray<T, D> {
663    fn deref_mut(&mut self) -> &mut Self::Target {
664        &mut self.robj
665    }
666}
667
668impl<T, D> From<Option<RArray<T, D>>> for Robj {
669    fn from(value: Option<RArray<T, D>>) -> Self {
670        match value {
671            None => nil_value(),
672            Some(value) => value.into(),
673        }
674    }
675}
676
677#[cfg(test)]
678mod tests {
679    use super::*;
680    use crate as extendr_api;
681    use extendr_engine::with_r;
682    use extendr_ffi::Rf_PrintValue;
683    use prelude::{Rcplx, Rfloat, Rint};
684
685    #[test]
686    fn test_empty_matrix_new() {
687        with_r(|| {
688            // These are arbitrarily filled. We cannot create assertions for them.
689            // let m: RMatrix<Rbyte> = RMatrix::new(5, 2); //   Error: Error: unimplemented type 'char' in 'eval'
690            // unsafe { Rf_PrintValue(m.get()) };
691            let m: RMatrix<Rbool> = RMatrix::new(5, 2);
692            unsafe { Rf_PrintValue(m.get()) };
693            let m: RMatrix<Rint> = RMatrix::new(5, 2);
694            unsafe { Rf_PrintValue(m.get()) };
695            let m: RMatrix<Rfloat> = RMatrix::new(5, 2);
696            unsafe { Rf_PrintValue(m.get()) };
697            let m: RMatrix<Rcplx> = RMatrix::new(5, 2);
698            unsafe { Rf_PrintValue(m.get()) };
699            rprintln!();
700
701            // let m: RMatrix<Rbyte> = RMatrix::new_with_na(10, 2); // not possible!
702            // unsafe { Rf_PrintValue(m.get()) };
703            let m: RMatrix<Rbool> = RMatrix::new_with_na(10, 2);
704            assert_eq!(R!("matrix(NA, 10, 2)").unwrap(), m.into_robj());
705
706            let m: RMatrix<Rint> = RMatrix::new_with_na(10, 2);
707            assert_eq!(R!("matrix(NA_integer_, 10, 2)").unwrap(), m.into_robj());
708
709            let m: RMatrix<Rfloat> = RMatrix::new_with_na(10, 2);
710            assert_eq!(R!("matrix(NA_real_, 10, 2)").unwrap(), m.into_robj());
711
712            let m: RMatrix<Rcplx> = RMatrix::new_with_na(10, 2);
713            assert_eq!(R!("matrix(NA_complex_, 10, 2)").unwrap(), m.into_robj());
714        });
715    }
716
717    #[test]
718    fn matrix_ops() {
719        test! {
720            let vector = RColumn::new_column(3, |r| [1., 2., 3.][r]);
721            let robj = r!(vector);
722            assert_eq!(robj.is_vector(), true);
723            assert_eq!(robj.nrows(), 3);
724
725            let vector2 : RColumn<f64> = robj.as_column().ok_or("expected array")?;
726            assert_eq!(vector2.data().len(), 3);
727            assert_eq!(vector2.nrows(), 3);
728
729            let matrix = RMatrix::new_matrix(3, 2, |r, c| [
730                [1., 2., 3.],
731                [4., 5., 6.]][c][r]);
732            let robj = r!(matrix);
733            assert_eq!(robj.is_matrix(), true);
734            assert_eq!(robj.nrows(), 3);
735            assert_eq!(robj.ncols(), 2);
736            let matrix2 : RMatrix<f64> = robj.as_matrix().ok_or("expected matrix")?;
737            assert_eq!(matrix2.data().len(), 6);
738            assert_eq!(matrix2.nrows(), 3);
739            assert_eq!(matrix2.ncols(), 2);
740
741            let array = RMatrix3D::new_matrix3d(2, 2, 2, |r, c, m| [
742                [[1., 2.],  [3., 4.]],
743                [[5.,  6.], [7., 8.]]][m][c][r]);
744            let robj = r!(array);
745            assert_eq!(robj.is_array(), true);
746            assert_eq!(robj.nrows(), 2);
747            assert_eq!(robj.ncols(), 2);
748            let array2 : RMatrix3D<f64> = robj.as_matrix3d().ok_or("expected matrix3d")?;
749            assert_eq!(array2.data().len(), 8);
750            assert_eq!(array2.nrows(), 2);
751            assert_eq!(array2.ncols(), 2);
752            assert_eq!(array2.nsub(), 2);
753        }
754    }
755
756    #[test]
757    fn test_from_vec_doubles_to_matrix() {
758        test! {
759        // R: pracma::magic(5) -> x
760        //    x[1:5**2]
761        // Thus `res` is a list of col-vectors.
762        let res: Vec<Doubles> = vec![
763            vec![17.0, 23.0, 4.0, 10.0, 11.0].try_into().unwrap(),
764            vec![24.0, 5.0, 6.0, 12.0, 18.0].try_into().unwrap(),
765            vec![1.0, 7.0, 13.0, 19.0, 25.0].try_into().unwrap(),
766            vec![8.0, 14.0, 20.0, 21.0, 2.0].try_into().unwrap(),
767            vec![15.0, 16.0, 22.0, 3.0, 9.0].try_into().unwrap(),
768        ];
769        let (n_x, n_y) = (5, 5);
770        let _matrix = RMatrix::new_matrix(n_x, n_y, |r, c| res[c][r]);
771
772        }
773    }
774}