1use self::robj::{AsTypedSlice, Robj};
3use super::*;
4use crate::throw_r_error;
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, const NDIM: usize> {
32 robj: Robj,
34
35 _data: std::marker::PhantomData<T>,
37}
38
39impl<T, const NDIM: usize> RArray<T, NDIM> {
40 pub fn get_dimnames(&self) -> List {
41 List::try_from(Robj::from_sexp(unsafe { Rf_GetArrayDimnames(self.get()) })).unwrap()
42 }
43
44 pub fn get_dim(&self) -> Vec<usize> {
48 self.robj
50 .get_attrib(wrapper::symbol::dim_symbol())
51 .unwrap()
52 .as_integer_vector()
53 .map(|vec| vec.into_iter().map(|x| x as usize).collect())
54 .unwrap()
55 }
56
57 pub fn set_names(&mut self, names: Strings) {
62 let _ = unsafe { Rf_namesgets(self.get_mut(), names.get()) };
64 }
65
66 pub fn set_dimnames(&mut self, dimnames: List) {
74 let _ = unsafe { Rf_dimnamesgets(self.get_mut(), dimnames.get()) };
75 }
76
77 pub fn set_dim(&mut self, dim: Robj) {
81 let _ = unsafe { Rf_dimgets(self.get_mut(), dim.get()) };
84 }
85}
86
87pub type RColumn<T> = RArray<T, 1>;
88pub type RMatrix<T> = RArray<T, 2>;
89pub type RMatrix3D<T> = RArray<T, 3>;
90pub type RMatrix4D<T> = RArray<T, 4>;
91pub type RMatrix5D<T> = RArray<T, 5>;
92
93impl<T, const NDIM: usize> RArray<T, NDIM>
96where
97 T: ToVectorValue,
98 Robj: for<'a> AsTypedSlice<'a, T>,
99{
100 pub fn new_array(dim: [usize; NDIM]) -> Self {
101 let sexptype = T::sexptype();
102 let len = dim.iter().product();
103 let mut robj = Robj::alloc_vector(sexptype, len);
104 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
105 RArray::from_parts(robj)
106 }
107}
108
109impl<T> RMatrix<T>
110where
111 T: ToVectorValue,
112 Robj: for<'a> AsTypedSlice<'a, T>,
113{
114 pub fn new(nrow: usize, ncol: usize) -> Self {
118 let sexptype = T::sexptype();
119 let matrix = Robj::alloc_matrix(sexptype, nrow as _, ncol as _);
120 RArray::from_parts(matrix)
121 }
122}
123
124impl<T> RMatrix<T>
125where
126 T: ToVectorValue + CanBeNA,
127 Robj: for<'a> AsTypedSlice<'a, T>,
128{
129 pub fn new_with_na(nrow: usize, ncol: usize) -> Self {
135 let mut matrix = Self::new(nrow, ncol);
136 if nrow != 0 || ncol != 0 {
137 matrix
138 .as_typed_slice_mut()
139 .unwrap()
140 .iter_mut()
141 .for_each(|x| {
142 *x = T::na();
143 });
144 }
145 matrix
146 }
147}
148
149impl<T> RMatrix<T> {
150 pub fn get_colnames(&self) -> Option<Strings> {
151 unsafe {
152 let maybe_colnames = Rf_GetColNames(Rf_GetArrayDimnames(self.get()));
153 match TYPEOF(maybe_colnames) {
154 SEXPTYPE::NILSXP => None,
155 SEXPTYPE::STRSXP => {
156 let colnames = Robj::from_sexp(maybe_colnames);
157 Some(std::mem::transmute(colnames))
158 }
159 _ => unreachable!(
160 "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
161 ),
162 }
163 }
164 }
165 pub fn get_rownames(&self) -> Option<Strings> {
166 unsafe {
167 let maybe_rownames = Rf_GetRowNames(Rf_GetArrayDimnames(self.get()));
168 match TYPEOF(maybe_rownames) {
169 SEXPTYPE::NILSXP => None,
170 SEXPTYPE::STRSXP => {
171 let rownames = Robj::from_sexp(maybe_rownames);
172 Some(std::mem::transmute(rownames))
173 }
174 _ => unreachable!(
175 "This should not have occurred. Please report an error at https://github.com/extendr/extendr/issues"
176 ),
177 }
178 }
179 }
180}
181
182const BASE: usize = 0;
183
184trait Offset<D> {
185 fn offset(&self, idx: D) -> usize;
187}
188
189impl<T, const NDIM: usize> Offset<[usize; NDIM]> for RArray<T, NDIM> {
190 fn offset(&self, index: [usize; NDIM]) -> usize {
192 let dims = self.get_dim();
193 if index.len() != dims.len() {
194 throw_r_error("array index: dimension mismatch");
195 }
196 index
197 .iter()
198 .zip(dims.iter())
199 .rev()
200 .enumerate()
201 .fold(0, |acc, (i, (&idx, &dim))| {
202 if idx - BASE >= dim {
203 let msg = format!("array index: dimension {} overflow (0-based dimension)", i);
204 throw_r_error(msg);
205 }
206 acc * dim + (idx - BASE)
207 })
208 }
209}
210
211impl<T, const NDIM: usize> RArray<T, NDIM>
212where
213 Robj: for<'a> AsTypedSlice<'a, T>,
214{
215 pub fn from_parts(robj: Robj) -> Self {
216 Self {
217 robj,
218 _data: std::marker::PhantomData,
219 }
220 }
221
222 pub fn data(&self) -> &[T] {
224 self.as_typed_slice().unwrap()
225 }
226
227 pub fn data_mut(&mut self) -> &mut [T] {
229 self.as_typed_slice_mut().unwrap()
230 }
231
232 pub fn ndim(&self) -> usize {
234 NDIM
235 }
236
237 pub fn dim(&self) -> Vec<usize> {
239 self.get_dim()
240 }
241}
242
243impl<T> RColumn<T>
244where
245 T: ToVectorValue,
246 Robj: for<'a> AsTypedSlice<'a, T>,
247{
248 pub fn new_column<F: FnMut(usize) -> T>(nrows: usize, f: F) -> Self {
250 let mut robj = (0..nrows).map(f).collect_robj();
251 let dim = [nrows];
252 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
253 RArray::from_parts(robj)
254 }
255
256 pub fn nrows(&self) -> usize {
258 self.get_dim()[0]
259 }
260}
261
262impl<T> RMatrix<T>
263where
264 T: ToVectorValue,
265 Robj: for<'a> AsTypedSlice<'a, T>,
266{
267 pub fn new_matrix<F: Clone + FnMut(usize, usize) -> T>(
279 nrows: usize,
280 ncols: usize,
281 f: F,
282 ) -> Self {
283 let mut robj = (0..ncols)
284 .flat_map(|c| {
285 let mut g = f.clone();
286 (0..nrows).map(move |r| g(r, c))
287 })
288 .collect_robj();
289 let dim = [nrows, ncols];
290 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
291 RArray::from_parts(robj)
292 }
293
294 pub fn nrows(&self) -> usize {
296 self.get_dim()[0]
297 }
298
299 pub fn ncols(&self) -> usize {
301 self.get_dim()[1]
302 }
303}
304
305impl<T> RMatrix3D<T>
306where
307 T: ToVectorValue,
308 Robj: for<'a> AsTypedSlice<'a, T>,
309{
310 pub fn new_matrix3d<F: Clone + FnMut(usize, usize, usize) -> T>(
311 nrows: usize,
312 ncols: usize,
313 nmatrix: usize,
314 f: F,
315 ) -> Self {
316 let mut robj = (0..nmatrix)
317 .flat_map(|m| {
318 let h = f.clone();
319 (0..ncols).flat_map(move |c| {
320 let mut g = h.clone();
321 (0..nrows).map(move |r| g(r, c, m))
322 })
323 })
324 .collect_robj();
325 let dim = [nrows, ncols, nmatrix];
326 robj.set_attrib(wrapper::symbol::dim_symbol(), dim).unwrap();
327 RArray::from_parts(robj)
328 }
329
330 pub fn nrows(&self) -> usize {
332 self.get_dim()[0]
333 }
334
335 pub fn ncols(&self) -> usize {
337 self.get_dim()[1]
338 }
339
340 pub fn nsub(&self) -> usize {
342 self.get_dim()[2]
343 }
344}
345
346impl<T> TryFrom<&Robj> for RColumn<T>
347where
348 Robj: for<'a> AsTypedSlice<'a, T>,
349{
350 type Error = Error;
351
352 fn try_from(robj: &Robj) -> Result<Self> {
353 if let Some(_slice) = robj.as_typed_slice() {
354 Ok(RArray::from_parts(robj.clone()))
355 } else {
356 Err(Error::ExpectedVector(robj.clone()))
357 }
358 }
359}
360
361impl<T> TryFrom<&Robj> for RMatrix<T>
362where
363 Robj: for<'a> AsTypedSlice<'a, T>,
364{
365 type Error = Error;
366
367 fn try_from(robj: &Robj) -> Result<Self> {
368 if !robj.is_matrix() {
369 Err(Error::ExpectedMatrix(robj.clone()))
370 } else if let Some(_slice) = robj.as_typed_slice() {
371 if let Some(dim) = robj.dim() {
372 let ndim = dim.len();
373 if ndim != 2 {
374 Err(Error::ExpectedMatrix(robj.clone()))
375 } else {
376 Ok(RArray::from_parts(robj.clone()))
377 }
378 } else {
379 Err(Error::ExpectedMatrix(robj.clone()))
380 }
381 } else {
382 Err(Error::TypeMismatch(robj.clone()))
383 }
384 }
385}
386
387impl<T> TryFrom<&Robj> for RMatrix3D<T>
388where
389 Robj: for<'a> AsTypedSlice<'a, T>,
390{
391 type Error = Error;
392
393 fn try_from(robj: &Robj) -> Result<Self> {
394 if let Some(_slice) = robj.as_typed_slice() {
395 if let Some(dim) = robj.dim() {
396 if dim.len() != 3 {
397 Err(Error::ExpectedMatrix3D(robj.clone()))
398 } else {
399 Ok(RArray::from_parts(robj.clone()))
400 }
401 } else {
402 Err(Error::ExpectedMatrix3D(robj.clone()))
403 }
404 } else {
405 Err(Error::TypeMismatch(robj.clone()))
406 }
407 }
408}
409
410impl<T> TryFrom<&Robj> for RMatrix4D<T>
411where
412 Robj: for<'a> AsTypedSlice<'a, T>,
413{
414 type Error = Error;
415
416 fn try_from(robj: &Robj) -> Result<Self> {
417 if let Some(_slice) = robj.as_typed_slice() {
418 if let Some(dim) = robj.dim() {
419 if dim.len() != 4 {
420 Err(Error::ExpectedMatrix4D(robj.clone()))
421 } else {
422 Ok(RArray::from_parts(robj.clone()))
423 }
424 } else {
425 Err(Error::ExpectedMatrix4D(robj.clone()))
426 }
427 } else {
428 Err(Error::TypeMismatch(robj.clone()))
429 }
430 }
431}
432
433impl<T> TryFrom<&Robj> for RMatrix5D<T>
434where
435 Robj: for<'a> AsTypedSlice<'a, T>,
436{
437 type Error = Error;
438
439 fn try_from(robj: &Robj) -> Result<Self> {
440 if let Some(_slice) = robj.as_typed_slice() {
441 if let Some(dim) = robj.dim() {
442 if dim.len() != 5 {
443 Err(Error::ExpectedMatrix5D(robj.clone()))
444 } else {
445 Ok(RArray::from_parts(robj.clone()))
446 }
447 } else {
448 Err(Error::ExpectedMatrix5D(robj.clone()))
449 }
450 } else {
451 Err(Error::TypeMismatch(robj.clone()))
452 }
453 }
454}
455
456macro_rules! impl_try_from_robj_ref {
457 ($($type : tt)*) => {
458 $(
459 impl<T> TryFrom<Robj> for $type<T>
460 where
461 Robj: for<'a> AsTypedSlice<'a, T>,
462 {
463 type Error = Error;
464
465 fn try_from(robj: Robj) -> Result<Self> {
466 <$type<T>>::try_from(&robj)
467 }
468 }
469
470 impl<T> TryFrom<&Robj> for Option<$type<T>>
471 where
472 Robj: for<'a> AsTypedSlice<'a, T>,
473 {
474 type Error = Error;
475
476 fn try_from(robj: &Robj) -> Result<Self> {
477 if robj.is_null() || robj.is_na() {
478 Ok(None)
479 } else {
480 Ok(Some(<$type<T>>::try_from(robj)?))
481 }
482 }
483 }
484
485 impl<T> TryFrom<Robj> for Option<$type<T>>
486 where
487 Robj: for<'a> AsTypedSlice<'a, T>,
488 {
489 type Error = Error;
490
491 fn try_from(robj: Robj) -> Result<Self> {
492 <Option::<$type<T>>>::try_from(&robj)
493 }
494 }
495 )*
496 }
497}
498
499impl_try_from_robj_ref!(
500 RMatrix
501 RColumn
502 RMatrix3D
503 RMatrix4D
504 RMatrix5D
505);
506
507impl<T, const DIM: usize> From<RArray<T, DIM>> for Robj {
508 fn from(array: RArray<T, DIM>) -> Self {
510 array.robj
511 }
512}
513
514pub trait MatrixConversions: GetSexp {
515 fn as_column<E>(&self) -> Option<RColumn<E>>
516 where
517 Robj: for<'a> AsTypedSlice<'a, E>,
518 {
519 <RColumn<E>>::try_from(self.as_robj()).ok()
520 }
521
522 fn as_matrix<E>(&self) -> Option<RMatrix<E>>
523 where
524 Robj: for<'a> AsTypedSlice<'a, E>,
525 {
526 <RMatrix<E>>::try_from(self.as_robj()).ok()
527 }
528
529 fn as_matrix3d<E>(&self) -> Option<RMatrix3D<E>>
530 where
531 Robj: for<'a> AsTypedSlice<'a, E>,
532 {
533 <RMatrix3D<E>>::try_from(self.as_robj()).ok()
534 }
535}
536
537impl MatrixConversions for Robj {}
538
539impl<T, const NDIM: usize> Index<[usize; NDIM]> for RArray<T, NDIM>
540where
541 Robj: for<'a> AsTypedSlice<'a, T>,
542{
543 type Output = T;
544
545 fn index(&self, index: [usize; NDIM]) -> &Self::Output {
568 unsafe {
569 self.data()
570 .as_ptr()
571 .add(self.offset(index))
572 .as_ref()
573 .unwrap()
574 }
575 }
576}
577
578impl<T, const NDIM: usize> IndexMut<[usize; NDIM]> for RArray<T, NDIM>
579where
580 Robj: for<'a> AsTypedSlice<'a, T>,
581{
582 fn index_mut(&mut self, index: [usize; NDIM]) -> &mut Self::Output {
605 unsafe {
606 self.data_mut()
607 .as_mut_ptr()
608 .add(self.offset(index))
609 .as_mut()
610 .unwrap()
611 }
612 }
613}
614
615impl<T, const NDIM: usize> Deref for RArray<T, NDIM> {
616 type Target = Robj;
617
618 fn deref(&self) -> &Self::Target {
619 &self.robj
620 }
621}
622
623impl<T, const NDIM: usize> DerefMut for RArray<T, NDIM> {
624 fn deref_mut(&mut self) -> &mut Self::Target {
625 &mut self.robj
626 }
627}
628
629impl<T, const NDIM: usize> From<Option<RArray<T, NDIM>>> for Robj {
630 fn from(value: Option<RArray<T, NDIM>>) -> Self {
631 match value {
632 None => nil_value(),
633 Some(value) => value.into(),
634 }
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use super::*;
641 use crate as extendr_api;
642 use extendr_engine::with_r;
643 use extendr_ffi::Rf_PrintValue;
644 use prelude::{Rcplx, Rfloat, Rint};
645
646 #[test]
647 fn test_empty_matrix_new() {
648 with_r(|| {
649 let m: RMatrix<Rbool> = RMatrix::new(5, 2);
653 unsafe { Rf_PrintValue(m.get()) };
654 let m: RMatrix<Rint> = RMatrix::new(5, 2);
655 unsafe { Rf_PrintValue(m.get()) };
656 let m: RMatrix<Rfloat> = RMatrix::new(5, 2);
657 unsafe { Rf_PrintValue(m.get()) };
658 let m: RMatrix<Rcplx> = RMatrix::new(5, 2);
659 unsafe { Rf_PrintValue(m.get()) };
660 rprintln!();
661
662 let m: RMatrix<Rbool> = RMatrix::new_with_na(10, 2);
665 assert_eq!(R!("matrix(NA, 10, 2)").unwrap(), m.into_robj());
666
667 let m: RMatrix<Rint> = RMatrix::new_with_na(10, 2);
668 assert_eq!(R!("matrix(NA_integer_, 10, 2)").unwrap(), m.into_robj());
669
670 let m: RMatrix<Rfloat> = RMatrix::new_with_na(10, 2);
671 assert_eq!(R!("matrix(NA_real_, 10, 2)").unwrap(), m.into_robj());
672
673 let m: RMatrix<Rcplx> = RMatrix::new_with_na(10, 2);
674 assert_eq!(R!("matrix(NA_complex_, 10, 2)").unwrap(), m.into_robj());
675 });
676 }
677
678 #[test]
679 fn matrix_ops() {
680 test! {
681 let vector = RColumn::new_column(3, |r| [1., 2., 3.][r]);
682 let robj = r!(vector);
683 assert_eq!(robj.is_vector(), true);
684 assert_eq!(robj.nrows(), 3);
685
686 let vector2 : RColumn<f64> = robj.as_column().ok_or("expected array")?;
687 assert_eq!(vector2.data().len(), 3);
688 assert_eq!(vector2.nrows(), 3);
689
690 let matrix = RMatrix::new_matrix(3, 2, |r, c| [
691 [1., 2., 3.],
692 [4., 5., 6.]][c][r]);
693 let robj = r!(matrix);
694 assert_eq!(robj.is_matrix(), true);
695 assert_eq!(robj.nrows(), 3);
696 assert_eq!(robj.ncols(), 2);
697 let matrix2 : RMatrix<f64> = robj.as_matrix().ok_or("expected matrix")?;
698 assert_eq!(matrix2.data().len(), 6);
699 assert_eq!(matrix2.nrows(), 3);
700 assert_eq!(matrix2.ncols(), 2);
701
702 let array = RMatrix3D::new_matrix3d(2, 2, 2, |r, c, m| [
703 [[1., 2.], [3., 4.]],
704 [[5., 6.], [7., 8.]]][m][c][r]);
705 let robj = r!(array);
706 assert_eq!(robj.is_array(), true);
707 assert_eq!(robj.nrows(), 2);
708 assert_eq!(robj.ncols(), 2);
709 let array2 : RMatrix3D<f64> = robj.as_matrix3d().ok_or("expected matrix3d")?;
710 assert_eq!(array2.data().len(), 8);
711 assert_eq!(array2.nrows(), 2);
712 assert_eq!(array2.ncols(), 2);
713 assert_eq!(array2.nsub(), 2);
714 }
715 }
716
717 #[test]
718 fn test_from_vec_doubles_to_matrix() {
719 test! {
720 let res: Vec<Doubles> = vec![
724 vec![17.0, 23.0, 4.0, 10.0, 11.0].try_into().unwrap(),
725 vec![24.0, 5.0, 6.0, 12.0, 18.0].try_into().unwrap(),
726 vec![1.0, 7.0, 13.0, 19.0, 25.0].try_into().unwrap(),
727 vec![8.0, 14.0, 20.0, 21.0, 2.0].try_into().unwrap(),
728 vec![15.0, 16.0, 22.0, 3.0, 9.0].try_into().unwrap(),
729 ];
730 let (n_x, n_y) = (5, 5);
731 let _matrix = RMatrix::new_matrix(n_x, n_y, |r, c| res[c][r]);
732
733 }
734 }
735}