1use self::robj::{AsTypedSlice, Robj};
3use super::*;
4use crate::scalar::{c64, Rbool, Rcplx, Rfloat, Rint};
5use crate::throw_r_error;
6use extendr_ffi::{
7 Rf_GetArrayDimnames, Rf_GetColNames, Rf_GetRowNames, Rf_dimgets, Rf_dimnamesgets, Rf_namesgets,
8 TYPEOF,
9};
10use std::ops::{Index, IndexMut};
11
12#[derive(Debug, PartialEq)]
32pub struct RArray<T, const NDIM: usize> {
33 robj: Robj,
35
36 _data: std::marker::PhantomData<T>,
38}
39
40impl<T, const NDIM: usize> RArray<T, NDIM> {
41 pub fn get_dimnames(&self) -> List {
42 unsafe { List::try_from(Robj::from_sexp(Rf_GetArrayDimnames(self.get()))).unwrap() }
43 }
44
45 pub fn get_dim(&self) -> Vec<usize> {
49 self.robj
50 .get_attrib(wrapper::symbol::dim_symbol())
51 .and_then(|dim| dim.as_integer_vector())
52 .map(|vec| vec.into_iter().map(|x| x as usize).collect())
53 .unwrap_or_else(|| vec![self.robj.len()])
54 }
55
56 pub fn set_names(&mut self, names: Strings) {
61 let _ = unsafe { Rf_namesgets(self.get_mut(), names.get()) };
63 }
64
65 pub fn set_dimnames(&mut self, dimnames: List) {
73 let _ = unsafe { Rf_dimnamesgets(self.get_mut(), dimnames.get()) };
74 }
75
76 pub fn set_dim(&mut self, dim: Robj) {
80 let _ = unsafe { Rf_dimgets(self.get_mut(), dim.get()) };
83 }
84}
85
86pub type RColumn<T> = RArray<T, 1>;
87pub type RMatrix<T> = RArray<T, 2>;
88pub type RMatrix3D<T> = RArray<T, 3>;
89pub type RMatrix4D<T> = RArray<T, 4>;
90pub type RMatrix5D<T> = RArray<T, 5>;
91
92impl<T, const NDIM: usize> RArray<T, NDIM>
95where
96 T: ToVectorValue,
97 Robj: for<'a> AsTypedSlice<'a, T>,
98{
99 pub fn new_array(dim: [usize; NDIM]) -> Self {
100 let sexptype = T::sexptype();
101 let len = dim.iter().product();
102 let mut robj = Robj::alloc_vector(sexptype, len);
103 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
104 RArray::from_parts(robj)
105 }
106}
107
108impl<T> RMatrix<T>
109where
110 T: ToVectorValue,
111 Robj: for<'a> AsTypedSlice<'a, T>,
112{
113 pub fn new(nrow: usize, ncol: usize) -> Self {
117 let sexptype = T::sexptype();
118 let matrix = Robj::alloc_matrix(sexptype, nrow as _, ncol as _);
119 RArray::from_parts(matrix)
120 }
121}
122
123impl<T> RMatrix<T>
124where
125 T: ToVectorValue + CanBeNA,
126 Robj: for<'a> AsTypedSlice<'a, T>,
127{
128 pub fn new_with_na(nrow: usize, ncol: usize) -> Self {
136 let mut matrix = Self::new(nrow, ncol);
137 if nrow != 0 || ncol != 0 {
138 matrix
139 .as_typed_slice_mut()
140 .unwrap()
141 .iter_mut()
142 .for_each(|x| {
143 *x = T::na();
144 });
145 }
146 matrix
147 }
148}
149
150impl<T> RMatrix<T> {
151 pub fn get_colnames(&self) -> Option<Strings> {
152 unsafe {
153 let maybe_colnames = Rf_GetColNames(Rf_GetArrayDimnames(self.get()));
154 match TYPEOF(maybe_colnames) {
155 SEXPTYPE::NILSXP => None,
156 SEXPTYPE::STRSXP => {
157 let colnames = Robj::from_sexp(maybe_colnames);
158 Strings::try_from(colnames).ok()
159 }
160 _ => unreachable!(
161 "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
162 ),
163 }
164 }
165 }
166 pub fn get_rownames(&self) -> Option<Strings> {
167 unsafe {
168 let maybe_rownames = Rf_GetRowNames(Rf_GetArrayDimnames(self.get()));
169 match TYPEOF(maybe_rownames) {
170 SEXPTYPE::NILSXP => None,
171 SEXPTYPE::STRSXP => {
172 let rownames = Robj::from_sexp(maybe_rownames);
173 Strings::try_from(rownames).ok()
174 }
175 _ => unreachable!(
176 "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
177 ),
178 }
179 }
180 }
181}
182
183const BASE: usize = 0;
184
185trait Offset<D> {
186 fn offset(&self, idx: D) -> usize;
188}
189
190impl<T, const NDIM: usize> Offset<[usize; NDIM]> for RArray<T, NDIM> {
191 fn offset(&self, index: [usize; NDIM]) -> usize {
193 let dims = self.get_dim();
194 if index.len() != dims.len() {
195 throw_r_error("array index: dimension mismatch");
196 }
197 index
198 .iter()
199 .zip(dims.iter())
200 .rev()
201 .enumerate()
202 .fold(0, |acc, (i, (&idx, &dim))| {
203 if idx - BASE >= dim {
204 let msg = format!("array index: dimension {} overflow (0-based dimension)", i);
205 throw_r_error(msg);
206 }
207 acc * dim + (idx - BASE)
208 })
209 }
210}
211
212impl<T, const NDIM: usize> RArray<T, NDIM>
213where
214 Robj: for<'a> AsTypedSlice<'a, T>,
215{
216 pub fn from_parts(robj: Robj) -> Self {
217 Self {
218 robj,
219 _data: std::marker::PhantomData,
220 }
221 }
222
223 pub fn data(&self) -> &[T] {
225 self.as_typed_slice().unwrap()
226 }
227
228 pub fn data_mut(&mut self) -> &mut [T] {
230 self.as_typed_slice_mut().unwrap()
231 }
232
233 pub fn ndim(&self) -> usize {
235 NDIM
236 }
237
238 pub fn dim(&self) -> Vec<usize> {
240 self.get_dim()
241 }
242}
243
244impl<T> RColumn<T>
245where
246 T: ToVectorValue,
247 Robj: for<'a> AsTypedSlice<'a, T>,
248{
249 pub fn new_column<F: FnMut(usize) -> T>(nrows: usize, f: F) -> Self {
251 let mut robj = (0..nrows).map(f).collect::<Robj>();
252 let dim = [nrows];
253 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
254 RArray::from_parts(robj)
255 }
256
257 pub fn nrows(&self) -> usize {
259 self.get_dim()[0]
260 }
261
262 pub fn iter(&self) -> impl Iterator<Item = &T> {
275 self.data().iter()
276 }
277
278 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
282 self.data_mut().iter_mut()
283 }
284}
285
286macro_rules! impl_rcolumn_into_wrapper {
287 ($scalar:ty, $wrapper:ty) => {
288 impl From<RColumn<$scalar>> for $wrapper {
289 fn from(col: RColumn<$scalar>) -> Self {
290 <$wrapper>::try_from(Robj::from(col)).unwrap()
291 }
292 }
293 };
294}
295
296impl_rcolumn_into_wrapper!(f64, Doubles);
297impl_rcolumn_into_wrapper!(Rfloat, Doubles);
298impl_rcolumn_into_wrapper!(i32, Integers);
299impl_rcolumn_into_wrapper!(Rint, Integers);
300impl_rcolumn_into_wrapper!(Rbool, Logicals);
301impl_rcolumn_into_wrapper!(c64, Complexes);
302impl_rcolumn_into_wrapper!(Rcplx, Complexes);
303impl_rcolumn_into_wrapper!(Rstr, Strings);
304impl_rcolumn_into_wrapper!(u8, Raw);
305
306impl<T> RMatrix<T>
307where
308 T: ToVectorValue,
309 Robj: for<'a> AsTypedSlice<'a, T>,
310{
311 pub fn new_matrix<F: Clone + FnMut(usize, usize) -> T>(
323 nrows: usize,
324 ncols: usize,
325 f: F,
326 ) -> Self {
327 let mut robj = (0..ncols)
328 .flat_map(|c| {
329 let mut g = f.clone();
330 (0..nrows).map(move |r| g(r, c))
331 })
332 .collect::<Robj>();
333 let dim = [nrows, ncols];
334 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
335 RArray::from_parts(robj)
336 }
337
338 pub fn nrows(&self) -> usize {
340 self.get_dim()[0]
341 }
342
343 pub fn ncols(&self) -> usize {
345 self.get_dim()[1]
346 }
347
348 pub fn row_iter(&self) -> RowIter<'_, T> {
369 let nrows = self.nrows();
370 let ncols = self.ncols();
371 RowIter {
372 data: self.data(),
373 range: 0..nrows,
374 nrows,
375 ncols,
376 }
377 }
378
379 pub fn col_iter(&self) -> ColIter<'_, T> {
399 let nrows = self.nrows();
400 let ncols = self.ncols();
401 ColIter {
402 data: self.data(),
403 range: 0..ncols,
404 nrows,
405 }
406 }
407}
408
409pub struct RowIter<'a, T> {
413 data: &'a [T],
414 range: std::ops::Range<usize>,
415 nrows: usize,
416 ncols: usize,
417}
418
419impl<'a, T> Iterator for RowIter<'a, T>
420where
421 T: ToVectorValue + Clone,
422 Robj: for<'b> AsTypedSlice<'b, T>,
423{
424 type Item = RColumn<T>;
425
426 fn next(&mut self) -> Option<Self::Item> {
427 let r = self.range.next()?;
428 Some(RColumn::new_column(self.ncols, |c| {
429 self.data[c * self.nrows + r].clone()
430 }))
431 }
432
433 fn size_hint(&self) -> (usize, Option<usize>) {
434 self.range.size_hint()
435 }
436}
437
438impl<'a, T> ExactSizeIterator for RowIter<'a, T>
439where
440 T: ToVectorValue + Clone,
441 Robj: for<'b> AsTypedSlice<'b, T>,
442{
443}
444
445pub struct ColIter<'a, T> {
449 data: &'a [T],
450 range: std::ops::Range<usize>,
451 nrows: usize,
452}
453
454impl<'a, T> Iterator for ColIter<'a, T>
455where
456 T: ToVectorValue + Clone,
457 Robj: for<'b> AsTypedSlice<'b, T>,
458{
459 type Item = RColumn<T>;
460
461 fn next(&mut self) -> Option<Self::Item> {
462 let c = self.range.next()?;
463 let start = c * self.nrows;
464 Some(RColumn::new_column(self.nrows, |r| {
465 self.data[start + r].clone()
466 }))
467 }
468
469 fn size_hint(&self) -> (usize, Option<usize>) {
470 self.range.size_hint()
471 }
472}
473
474impl<'a, T> ExactSizeIterator for ColIter<'a, T>
475where
476 T: ToVectorValue + Clone,
477 Robj: for<'b> AsTypedSlice<'b, T>,
478{
479}
480
481impl<T> RMatrix3D<T>
482where
483 T: ToVectorValue,
484 Robj: for<'a> AsTypedSlice<'a, T>,
485{
486 pub fn new_matrix3d<F: Clone + FnMut(usize, usize, usize) -> T>(
487 nrows: usize,
488 ncols: usize,
489 nmatrix: usize,
490 f: F,
491 ) -> Self {
492 let mut robj = (0..nmatrix)
493 .flat_map(|m| {
494 let h = f.clone();
495 (0..ncols).flat_map(move |c| {
496 let mut g = h.clone();
497 (0..nrows).map(move |r| g(r, c, m))
498 })
499 })
500 .collect::<Robj>();
501 let dim = [nrows, ncols, nmatrix];
502 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
503 RArray::from_parts(robj)
504 }
505
506 pub fn nrows(&self) -> usize {
508 self.get_dim()[0]
509 }
510
511 pub fn ncols(&self) -> usize {
513 self.get_dim()[1]
514 }
515
516 pub fn nsub(&self) -> usize {
518 self.get_dim()[2]
519 }
520}
521
522impl<T> TryFrom<&Robj> for RColumn<T>
523where
524 Robj: for<'a> AsTypedSlice<'a, T>,
525{
526 type Error = Error;
527
528 fn try_from(robj: &Robj) -> Result<Self> {
529 if let Some(_slice) = robj.as_typed_slice() {
530 Ok(RArray::from_parts(robj.clone()))
531 } else {
532 Err(Error::ExpectedVector(robj.clone()))
533 }
534 }
535}
536
537impl<T> TryFrom<&Robj> for RMatrix<T>
538where
539 Robj: for<'a> AsTypedSlice<'a, T>,
540{
541 type Error = Error;
542
543 fn try_from(robj: &Robj) -> Result<Self> {
544 if !robj.is_matrix() {
545 Err(Error::ExpectedMatrix(robj.clone()))
546 } else if let Some(_slice) = robj.as_typed_slice() {
547 if let Some(dim) = robj.dim() {
548 let ndim = dim.len();
549 if ndim != 2 {
550 Err(Error::ExpectedMatrix(robj.clone()))
551 } else {
552 Ok(RArray::from_parts(robj.clone()))
553 }
554 } else {
555 Err(Error::ExpectedMatrix(robj.clone()))
556 }
557 } else {
558 Err(Error::TypeMismatch(robj.clone()))
559 }
560 }
561}
562
563impl<T> TryFrom<&Robj> for RMatrix3D<T>
564where
565 Robj: for<'a> AsTypedSlice<'a, T>,
566{
567 type Error = Error;
568
569 fn try_from(robj: &Robj) -> Result<Self> {
570 if let Some(_slice) = robj.as_typed_slice() {
571 if let Some(dim) = robj.dim() {
572 if dim.len() != 3 {
573 Err(Error::ExpectedMatrix3D(robj.clone()))
574 } else {
575 Ok(RArray::from_parts(robj.clone()))
576 }
577 } else {
578 Err(Error::ExpectedMatrix3D(robj.clone()))
579 }
580 } else {
581 Err(Error::TypeMismatch(robj.clone()))
582 }
583 }
584}
585
586impl<T> TryFrom<&Robj> for RMatrix4D<T>
587where
588 Robj: for<'a> AsTypedSlice<'a, T>,
589{
590 type Error = Error;
591
592 fn try_from(robj: &Robj) -> Result<Self> {
593 if let Some(_slice) = robj.as_typed_slice() {
594 if let Some(dim) = robj.dim() {
595 if dim.len() != 4 {
596 Err(Error::ExpectedMatrix4D(robj.clone()))
597 } else {
598 Ok(RArray::from_parts(robj.clone()))
599 }
600 } else {
601 Err(Error::ExpectedMatrix4D(robj.clone()))
602 }
603 } else {
604 Err(Error::TypeMismatch(robj.clone()))
605 }
606 }
607}
608
609impl<T> TryFrom<&Robj> for RMatrix5D<T>
610where
611 Robj: for<'a> AsTypedSlice<'a, T>,
612{
613 type Error = Error;
614
615 fn try_from(robj: &Robj) -> Result<Self> {
616 if let Some(_slice) = robj.as_typed_slice() {
617 if let Some(dim) = robj.dim() {
618 if dim.len() != 5 {
619 Err(Error::ExpectedMatrix5D(robj.clone()))
620 } else {
621 Ok(RArray::from_parts(robj.clone()))
622 }
623 } else {
624 Err(Error::ExpectedMatrix5D(robj.clone()))
625 }
626 } else {
627 Err(Error::TypeMismatch(robj.clone()))
628 }
629 }
630}
631
632macro_rules! impl_try_from_robj_ref {
633 ($($type : tt)*) => {
634 $(
635 impl<T> TryFrom<Robj> for $type<T>
636 where
637 Robj: for<'a> AsTypedSlice<'a, T>,
638 {
639 type Error = Error;
640
641 fn try_from(robj: Robj) -> Result<Self> {
642 <$type<T>>::try_from(&robj)
643 }
644 }
645
646 impl<T> TryFrom<&Robj> for Option<$type<T>>
647 where
648 Robj: for<'a> AsTypedSlice<'a, T>,
649 {
650 type Error = Error;
651
652 fn try_from(robj: &Robj) -> Result<Self> {
653 if robj.is_null() || robj.is_na() {
654 Ok(None)
655 } else {
656 Ok(Some(<$type<T>>::try_from(robj)?))
657 }
658 }
659 }
660
661 impl<T> TryFrom<Robj> for Option<$type<T>>
662 where
663 Robj: for<'a> AsTypedSlice<'a, T>,
664 {
665 type Error = Error;
666
667 fn try_from(robj: Robj) -> Result<Self> {
668 <Option::<$type<T>>>::try_from(&robj)
669 }
670 }
671 )*
672 }
673}
674
675impl_try_from_robj_ref!(
676 RMatrix
677 RColumn
678 RMatrix3D
679 RMatrix4D
680 RMatrix5D
681);
682
683impl<T, const DIM: usize> From<RArray<T, DIM>> for Robj {
684 fn from(array: RArray<T, DIM>) -> Self {
686 array.robj
687 }
688}
689
690pub trait MatrixConversions: GetSexp {
691 fn as_column<E>(&self) -> Option<RColumn<E>>
692 where
693 Robj: for<'a> AsTypedSlice<'a, E>,
694 {
695 <RColumn<E>>::try_from(self.as_robj()).ok()
696 }
697
698 fn as_matrix<E>(&self) -> Option<RMatrix<E>>
699 where
700 Robj: for<'a> AsTypedSlice<'a, E>,
701 {
702 <RMatrix<E>>::try_from(self.as_robj()).ok()
703 }
704
705 fn as_matrix3d<E>(&self) -> Option<RMatrix3D<E>>
706 where
707 Robj: for<'a> AsTypedSlice<'a, E>,
708 {
709 <RMatrix3D<E>>::try_from(self.as_robj()).ok()
710 }
711}
712
713impl MatrixConversions for Robj {}
714
715impl<T, const NDIM: usize> Index<[usize; NDIM]> for RArray<T, NDIM>
716where
717 Robj: for<'a> AsTypedSlice<'a, T>,
718{
719 type Output = T;
720
721 fn index(&self, index: [usize; NDIM]) -> &Self::Output {
744 unsafe {
745 self.data()
746 .as_ptr()
747 .add(self.offset(index))
748 .as_ref()
749 .unwrap()
750 }
751 }
752}
753
754impl<T, const NDIM: usize> IndexMut<[usize; NDIM]> for RArray<T, NDIM>
755where
756 Robj: for<'a> AsTypedSlice<'a, T>,
757{
758 fn index_mut(&mut self, index: [usize; NDIM]) -> &mut Self::Output {
781 unsafe {
782 self.data_mut()
783 .as_mut_ptr()
784 .add(self.offset(index))
785 .as_mut()
786 .unwrap()
787 }
788 }
789}
790
791impl<T, const NDIM: usize> Deref for RArray<T, NDIM> {
792 type Target = Robj;
793
794 fn deref(&self) -> &Self::Target {
795 &self.robj
796 }
797}
798
799impl<T, const NDIM: usize> DerefMut for RArray<T, NDIM> {
800 fn deref_mut(&mut self) -> &mut Self::Target {
801 &mut self.robj
802 }
803}
804
805impl<T, const NDIM: usize> From<Option<RArray<T, NDIM>>> for Robj {
806 fn from(value: Option<RArray<T, NDIM>>) -> Self {
807 match value {
808 None => nil_value(),
809 Some(value) => value.into(),
810 }
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use std::error::Error;
817
818 use super::*;
819 use crate as extendr_api;
820 use extendr_engine::with_r;
821 use extendr_ffi::Rf_PrintValue;
822 use prelude::{Rcplx, Rfloat, Rint};
823
824 #[test]
825 fn test_empty_matrix_new() -> std::result::Result<(), Box<dyn Error>> {
826 with_r(|| {
827 let m: RMatrix<Rbool> = RMatrix::new(5, 2);
831 unsafe { Rf_PrintValue(m.get()) };
832 let m: RMatrix<Rint> = RMatrix::new(5, 2);
833 unsafe { Rf_PrintValue(m.get()) };
834 let m: RMatrix<Rfloat> = RMatrix::new(5, 2);
835 unsafe { Rf_PrintValue(m.get()) };
836 let m: RMatrix<Rcplx> = RMatrix::new(5, 2);
837 unsafe { Rf_PrintValue(m.get()) };
838 rprintln!();
839
840 let m: RMatrix<Rbool> = RMatrix::new_with_na(10, 2);
843 assert_eq!(R!("matrix(NA, 10, 2)").unwrap(), m.into_robj());
844
845 let m: RMatrix<Rint> = RMatrix::new_with_na(10, 2);
846 assert_eq!(R!("matrix(NA_integer_, 10, 2)").unwrap(), m.into_robj());
847
848 let m: RMatrix<Rfloat> = RMatrix::new_with_na(10, 2);
849 assert_eq!(R!("matrix(NA_real_, 10, 2)").unwrap(), m.into_robj());
850
851 let m: RMatrix<Rcplx> = RMatrix::new_with_na(10, 2);
852 assert_eq!(R!("matrix(NA_complex_, 10, 2)").unwrap(), m.into_robj());
853 Ok(())
854 })
855 }
856
857 #[test]
858 fn matrix_ops() {
859 test! {
860 let vector = RColumn::new_column(3, |r| [1., 2., 3.][r]);
861 let robj = r!(vector);
862 assert_eq!(robj.is_vector(), true);
863 assert_eq!(robj.nrows(), 3);
864
865 let vector2 : RColumn<f64> = robj.as_column().ok_or("expected array")?;
866 assert_eq!(vector2.data().len(), 3);
867 assert_eq!(vector2.nrows(), 3);
868
869 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
870 [1., 2., 3.],
871 [4., 5., 6.]][c][r]);
872 let robj = r!(matrix);
873 assert_eq!(robj.is_matrix(), true);
874 assert_eq!(robj.nrows(), 3);
875 assert_eq!(robj.ncols(), 2);
876 let matrix2 : RMatrix<f64> = robj.as_matrix().ok_or("expected matrix")?;
877 assert_eq!(matrix2.data().len(), 6);
878 assert_eq!(matrix2.nrows(), 3);
879 assert_eq!(matrix2.ncols(), 2);
880
881 let array = RMatrix3D::new_matrix3d(2, 2, 2, |r, c, m| [
882 [[1., 2.], [3., 4.]],
883 [[5., 6.], [7., 8.]]][m][c][r]);
884 let robj = r!(array);
885 assert_eq!(robj.is_array(), true);
886 assert_eq!(robj.nrows(), 2);
887 assert_eq!(robj.ncols(), 2);
888 let array2 : RMatrix3D<f64> = robj.as_matrix3d().ok_or("expected matrix3d")?;
889 assert_eq!(array2.data().len(), 8);
890 assert_eq!(array2.nrows(), 2);
891 assert_eq!(array2.ncols(), 2);
892 assert_eq!(array2.nsub(), 2);
893 }
894 }
895
896 #[test]
897 fn test_from_vec_doubles_to_matrix() {
898 test! {
899 let res: Vec<Doubles> = vec![
903 vec![17.0, 23.0, 4.0, 10.0, 11.0].try_into().unwrap(),
904 vec![24.0, 5.0, 6.0, 12.0, 18.0].try_into().unwrap(),
905 vec![1.0, 7.0, 13.0, 19.0, 25.0].try_into().unwrap(),
906 vec![8.0, 14.0, 20.0, 21.0, 2.0].try_into().unwrap(),
907 vec![15.0, 16.0, 22.0, 3.0, 9.0].try_into().unwrap(),
908 ];
909 let (n_x, n_y) = (5, 5);
910 let _matrix = RMatrix::new_matrix(n_x, n_y, |r, c| res[c][r]);
911
912 }
913 }
914
915 #[test]
916 fn test_rmatrix_with_rstr() {
917 test! {
918 let string_data = ["a", "b", "c", "d", "e", "f"];
920 let matrix: RMatrix<Rstr> = RMatrix::new_matrix(2, 3, |r, c| {
921 let idx = c * 2 + r;
922 Rstr::from(string_data[idx])
923 });
924
925 assert_eq!(matrix.nrows(), 2);
926 assert_eq!(matrix.ncols(), 3);
927
928 let robj: Robj = matrix.into();
930 assert_eq!(robj.is_matrix(), true);
931 assert_eq!(robj.rtype(), Rtype::Strings);
932 }
933 }
934
935 #[test]
936 fn test_row_iter_values() {
937 test! {
938 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
939 [1., 2., 3.],
940 [4., 5., 6.]][c][r]);
941 let rows: Vec<_> = matrix.row_iter().collect();
942 assert_eq!(rows.len(), 3);
943 assert_eq!(rows[0].data(), &[1., 4.]);
944 assert_eq!(rows[1].data(), &[2., 5.]);
945 assert_eq!(rows[2].data(), &[3., 6.]);
946 }
947 }
948
949 #[test]
950 fn test_col_iter_values() {
951 test! {
952 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
953 [1., 2., 3.],
954 [4., 5., 6.]][c][r]);
955 let cols: Vec<_> = matrix.col_iter().collect();
956 assert_eq!(cols.len(), 2);
957 assert_eq!(cols[0].data(), &[1., 2., 3.]);
958 assert_eq!(cols[1].data(), &[4., 5., 6.]);
959 }
960 }
961
962 #[test]
963 fn test_row_iter_with_rcolumn_iter() {
964 test! {
965 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
966 [1., 2., 3.],
967 [4., 5., 6.]][c][r]);
968 let row_sums: Vec<f64> = matrix
969 .row_iter()
970 .map(|row| row.iter().sum::<f64>())
971 .collect();
972 assert_eq!(row_sums, vec![5., 7., 9.]);
973 }
974 }
975
976 #[test]
977 fn test_empty_matrix_iters() {
978 test! {
979 let no_rows: RMatrix<f64> = RMatrix::new_matrix(0, 3, |_, _| 0.);
980 assert_eq!(no_rows.row_iter().count(), 0);
981 assert_eq!(no_rows.col_iter().count(), 3);
982 for col in no_rows.col_iter() {
983 assert_eq!(col.nrows(), 0);
984 }
985
986 let no_cols: RMatrix<f64> = RMatrix::new_matrix(3, 0, |_, _| 0.);
987 assert_eq!(no_cols.row_iter().count(), 3);
988 assert_eq!(no_cols.col_iter().count(), 0);
989 for row in no_cols.row_iter() {
990 assert_eq!(row.nrows(), 0);
991 }
992 }
993 }
994
995 #[test]
996 fn test_rfloat_matrix_iters() {
997 test! {
998 let matrix = RMatrix::new_matrix(2, 2, |r, c| {
999 Rfloat::from([[1., 2.], [3., 4.]][c][r])
1000 });
1001 let rows: Vec<_> = matrix.row_iter().collect();
1002 assert_eq!(rows.len(), 2);
1003 assert_eq!(rows[0].data(), &[Rfloat::from(1.), Rfloat::from(3.)]);
1004 assert_eq!(rows[1].data(), &[Rfloat::from(2.), Rfloat::from(4.)]);
1005
1006 let cols: Vec<_> = matrix.col_iter().collect();
1007 assert_eq!(cols.len(), 2);
1008 assert_eq!(cols[0].data(), &[Rfloat::from(1.), Rfloat::from(2.)]);
1009 assert_eq!(cols[1].data(), &[Rfloat::from(3.), Rfloat::from(4.)]);
1010 }
1011 }
1012
1013 #[test]
1014 fn test_rcolumn_without_dim() {
1015 test! {
1016 let robj = r!([1., 2., 3.]);
1017 let column = RColumn::<f64>::try_from(&robj)?;
1018 assert_eq!(column.get_dim(), vec![3]);
1019 assert_eq!(column.nrows(), 3);
1020 assert_eq!(column[[1]], 2.);
1021 }
1022 }
1023
1024 #[test]
1025 fn test_row_col_iter_roundtrip() {
1026 test! {
1027 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
1028 [1., 2., 3.],
1029 [4., 5., 6.]][c][r]);
1030
1031 let first_row = matrix.row_iter().next().unwrap();
1032 let robj: Robj = first_row.into();
1033 assert!(robj.is_real());
1034 assert_eq!(robj.len(), 2);
1035 assert_eq!(robj.as_real_slice().unwrap(), &[1., 4.]);
1036
1037 let first_col = matrix.col_iter().next().unwrap();
1038 let robj: Robj = first_col.into();
1039 assert!(robj.is_real());
1040 assert_eq!(robj.len(), 3);
1041 assert_eq!(robj.as_real_slice().unwrap(), &[1., 2., 3.]);
1042
1043 let string_data = ["a", "b", "c", "d"];
1044 let str_matrix: RMatrix<Rstr> = RMatrix::new_matrix(2, 2, |r, c| {
1045 Rstr::from(string_data[c * 2 + r])
1046 });
1047 let first_row = str_matrix.row_iter().next().unwrap();
1048 let robj: Robj = first_row.into();
1049 assert!(robj.is_string());
1050 assert_eq!(robj.len(), 2);
1051 assert_eq!(robj.as_str_vector().unwrap(), vec!["a", "c"]);
1052 }
1053 }
1054
1055 #[test]
1056 fn test_rcolumn_iter_mut() {
1057 test! {
1058 let mut col = RColumn::new_column(3, |r| [1., 2., 3.][r]);
1059 col.iter_mut().for_each(|v| *v += 10.);
1060 assert_eq!(col.data(), &[11., 12., 13.]);
1061
1062 let matrix = RMatrix::new_matrix(2, 2, |r, c| [[1., 2.], [3., 4.]][c][r]);
1063 let mut first_row = matrix.row_iter().next().unwrap();
1064 first_row.iter_mut().for_each(|v| *v *= 2.);
1065 assert_eq!(first_row.data(), &[2., 6.]);
1066
1067 assert_eq!(matrix[[0, 0]], 1.);
1069 assert_eq!(matrix[[0, 1]], 3.);
1070 }
1071 }
1072
1073 #[test]
1074 fn test_row_iter_to_doubles() {
1075 test! {
1076 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
1077 [1., 2., 3.],
1078 [4., 5., 6.]][c][r]);
1079
1080 let rows: Vec<Doubles> = matrix.row_iter().map(Doubles::from).collect();
1081
1082 assert_eq!(rows.len(), 3);
1083 assert_eq!(rows[0].iter().collect::<Vec<_>>(), vec![Rfloat::from(1.), Rfloat::from(4.)]);
1084 assert_eq!(rows[1].iter().collect::<Vec<_>>(), vec![Rfloat::from(2.), Rfloat::from(5.)]);
1085 assert_eq!(rows[2].iter().collect::<Vec<_>>(), vec![Rfloat::from(3.), Rfloat::from(6.)]);
1086 }
1087 }
1088
1089 #[test]
1090 fn test_row_iter_to_strings() {
1091 test! {
1092 let string_data = ["a", "b", "c", "d"];
1093 let str_matrix: RMatrix<Rstr> = RMatrix::new_matrix(2, 2, |r, c| {
1094 Rstr::from(string_data[c * 2 + r])
1095 });
1096
1097 let rows: Vec<Strings> = str_matrix.row_iter().map(Strings::from).collect();
1098
1099 assert_eq!(rows.len(), 2);
1100 assert_eq!(rows[0].iter().map(|s| s.as_ref()).collect::<Vec<_>>(), vec!["a", "c"]);
1101 assert_eq!(rows[1].iter().map(|s| s.as_ref()).collect::<Vec<_>>(), vec!["b", "d"]);
1102 }
1103 }
1104}