1use 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#[derive(Debug, PartialEq)]
31pub struct RArray<T, D> {
32 robj: Robj,
34
35 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 pub fn set_names(&mut self, names: Strings) {
51 let _ = unsafe { Rf_namesgets(self.get_mut(), names.get()) };
53 }
54
55 pub fn set_dimnames(&mut self, dimnames: List) {
63 let _ = unsafe { Rf_dimnamesgets(self.get_mut(), dimnames.get()) };
64 }
65
66 pub fn set_dim(&mut self, dim: Robj) {
70 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 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 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 fn offset(&self, idx: D) -> usize;
160}
161
162impl<T> Offset<[usize; 1]> for RArray<T, [usize; 1]> {
163 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 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 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 pub fn data(&self) -> &[T] {
215 self.as_typed_slice().unwrap()
216 }
217
218 pub fn data_mut(&mut self) -> &mut [T] {
220 self.as_typed_slice_mut().unwrap()
221 }
222
223 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 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 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 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 pub fn nrows(&self) -> usize {
282 self.dim[0]
283 }
284
285 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 pub fn nrows(&self) -> usize {
318 self.dim[0]
319 }
320
321 pub fn ncols(&self) -> usize {
323 self.dim[1]
324 }
325
326 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 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 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 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 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 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 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<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 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}