The R Project SVN R

Rev

Rev 72114 | Rev 72835 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 72114 Rev 72834
Line 30... Line 30...
30
#include <R_ext/Applic.h> /* for dgemm */
30
#include <R_ext/Applic.h> /* for dgemm */
31
#include <R_ext/Itermacros.h>
31
#include <R_ext/Itermacros.h>
32
 
32
 
33
#include "duplicate.h"
33
#include "duplicate.h"
34
 
34
 
-
 
35
#include <complex.h>
-
 
36
#include "Rcomplex.h"	/* toC99 */
-
 
37
 
35
/* "GetRowNames" and "GetColNames" are utility routines which
38
/* "GetRowNames" and "GetColNames" are utility routines which
36
 * locate and return the row names and column names from the
39
 * locate and return the row names and column names from the
37
 * dimnames attribute of a matrix.  They are useful because
40
 * dimnames attribute of a matrix.  They are useful because
38
 * old versions of R used pair-based lists for dimnames
41
 * old versions of R used pair-based lists for dimnames
39
 * whereas recent versions use vector based lists.
42
 * whereas recent versions use vector based lists.
Line 487... Line 490...
487
    double *ans_elt;
490
    double *ans_elt;
488
 
491
 
489
    x_len = dispatch_xlength(x, call, rho);
492
    x_len = dispatch_xlength(x, call, rho);
490
    PROTECT(ans = allocVector(REALSXP, x_len));
493
    PROTECT(ans = allocVector(REALSXP, x_len));
491
    for (i = 0, ans_elt = REAL(ans); i < x_len; i++, ans_elt++)
494
    for (i = 0, ans_elt = REAL(ans); i < x_len; i++, ans_elt++)
492
        *ans_elt = getElementLength(x, i, call, rho);
495
        *ans_elt = (double) getElementLength(x, i, call, rho);
493
    UNPROTECT(1);
496
    UNPROTECT(1);
494
    return ans;
497
    return ans;
495
}
498
}
496
#endif
499
#endif
497
 
500
 
Line 619... Line 622...
619
	if (!R_FINITE(x[i]+x[i+1]))
622
	if (!R_FINITE(x[i]+x[i+1]))
620
	    return TRUE;
623
	    return TRUE;
621
    return FALSE;
624
    return FALSE;
622
}
625
}
623
 
626
 
-
 
627
/*
-
 
628
 This is an experimental version that has been observed to run fast on some
-
 
629
 SIMD hardware with GCC and ICC.
-
 
630
 Note that the OpenMP reduction assumes associativity of addition, which is
-
 
631
 safe here, because the result is only used for an imprecise test for
-
 
632
 the presence of NaN and Inf values.
-
 
633
*/
-
 
634
static Rboolean mayHaveNaNOrInf_simd(double *x, R_xlen_t n)
-
 
635
{
-
 
636
    double s = 0;
-
 
637
    /* SIMD reduction is supported since OpenMP 4.0. The value of _OPENMP is
-
 
638
       unreliable in some compilers, so we depend on HAVE_OPENMP_SIMDRED,
-
 
639
       which is normally set by configure based on a test. */
-
 
640
    /* _OPENMP >= 201307 */
-
 
641
#if defined(_OPENMP) && HAVE_OPENMP_SIMDRED
-
 
642
    #pragma omp simd reduction(+:s)
-
 
643
#endif
-
 
644
    for (R_xlen_t i = 0; i < n; i++)
-
 
645
	s += x[i];
-
 
646
    return !R_FINITE(s);
-
 
647
}
-
 
648
 
624
static Rboolean cmayHaveNaNOrInf(Rcomplex *x, R_xlen_t n)
649
static Rboolean cmayHaveNaNOrInf(Rcomplex *x, R_xlen_t n)
625
{
650
{
626
    /* With HAVE_FORTRAN_DOUBLE_COMPLEX set, it should be clear that
651
    /* With HAVE_FORTRAN_DOUBLE_COMPLEX set, it should be clear that
627
       Rcomplex has no padding, so we could probably use mayHaveNaNOrInf,
652
       Rcomplex has no padding, so we could probably use mayHaveNaNOrInf,
628
       but better safe than sorry... */
653
       but better safe than sorry... */
Line 632... Line 657...
632
	if (!R_FINITE(x[i].r+x[i].i+x[i+1].r+x[i+1].i))
657
	if (!R_FINITE(x[i].r+x[i].i+x[i+1].r+x[i+1].i))
633
	    return TRUE;
658
	    return TRUE;
634
    return FALSE;
659
    return FALSE;
635
}
660
}
636
 
661
 
637
static R_INLINE void simple_matprod(double *x, int nrx, int ncx,
662
/* experimental version for SIMD hardware (see also mayHaveNaNOrInf_simd) */
638
                                    double *y, int nry, int ncy, double *z)
663
static Rboolean cmayHaveNaNOrInf_simd(Rcomplex *x, R_xlen_t n)
639
{
664
{
640
    LDOUBLE sum;
665
    double s = 0;
-
 
666
    /* _OPENMP >= 201307 - see mayHaveNaNOrInf_simd */
-
 
667
#if defined(_OPENMP) && HAVE_OPENMP_SIMDRED
-
 
668
    #pragma omp simd reduction(+:s)
-
 
669
#endif
641
    R_xlen_t NRX = nrx, NRY = nry;
670
    for (R_xlen_t i = 0; i < n; i++) {
-
 
671
	s += x[i].r;
-
 
672
	s += x[i].i;
-
 
673
    }
-
 
674
    return !R_FINITE(s);
-
 
675
}
642
 
676
 
-
 
677
static void internal_matprod(double *x, int nrx, int ncx,
-
 
678
                             double *y, int nry, int ncy, double *z)
-
 
679
{
-
 
680
    LDOUBLE sum;
-
 
681
#define MATPROD_BODY					\
-
 
682
    R_xlen_t NRX = nrx, NRY = nry;			\
643
    for (int i = 0; i < nrx; i++)
683
    for (int i = 0; i < nrx; i++)			\
644
	for (int k = 0; k < ncy; k++) {
684
	for (int k = 0; k < ncy; k++) {			\
645
	    sum = 0.0;
685
	    sum = 0.0;					\
646
	    for (int j = 0; j < ncx; j++)
686
	    for (int j = 0; j < ncx; j++)		\
647
		sum += x[i + j * NRX] * y[j + k * NRY];
687
		sum += x[i + j * NRX] * y[j + k * NRY];	\
648
	    z[i + k * NRX] = (double) sum;
688
	    z[i + k * NRX] = (double) sum;		\
649
	}
689
	}
-
 
690
    MATPROD_BODY;
-
 
691
}
-
 
692
 
-
 
693
static void simple_matprod(double *x, int nrx, int ncx,
-
 
694
                           double *y, int nry, int ncy, double *z)
-
 
695
{
-
 
696
    double sum;
-
 
697
    MATPROD_BODY;
650
}
698
}
651
 
699
 
652
static R_INLINE void simple_crossprod(double *x, int nrx, int ncx,
700
static void internal_crossprod(double *x, int nrx, int ncx,
653
                                      double *y, int nry, int ncy, double *z)
701
                               double *y, int nry, int ncy, double *z)
654
{
702
{
655
    LDOUBLE sum;
703
    LDOUBLE sum;
-
 
704
#define CROSSPROD_BODY					\
656
    R_xlen_t NRX = nrx, NRY = nry, NCX = ncx;
705
    R_xlen_t NRX = nrx, NRY = nry, NCX = ncx;		\
657
    for (int i = 0; i < ncx; i++)
706
    for (int i = 0; i < ncx; i++)			\
658
	for (int k = 0; k < ncy; k++) {
707
	for (int k = 0; k < ncy; k++) {			\
659
	    sum = 0.0;
708
	    sum = 0.0;					\
660
	    for (int j = 0; j < nrx; j++)
709
	    for (int j = 0; j < nrx; j++)		\
661
		sum += x[j + i * NRX] * y[j + k * NRY];
710
		sum += x[j + i * NRX] * y[j + k * NRY];	\
662
	    z[i + k * NCX] = (double) sum;
711
	    z[i + k * NCX] = (double) sum;		\
663
	}
712
	}
-
 
713
    CROSSPROD_BODY;
664
}
714
}
665
 
715
 
666
static R_INLINE void simple_tcrossprod(double *x, int nrx, int ncx,
716
static void simple_crossprod(double *x, int nrx, int ncx,
667
                                       double *y, int nry, int ncy, double *z)
717
                             double *y, int nry, int ncy, double *z)
668
{
718
{
669
    LDOUBLE sum;
719
    double sum;
670
    R_xlen_t NRX = nrx, NRY = nry;
720
    CROSSPROD_BODY;
-
 
721
}
671
 
722
 
-
 
723
static void internal_tcrossprod(double *x, int nrx, int ncx,
-
 
724
                                double *y, int nry, int ncy, double *z)
-
 
725
{
-
 
726
    LDOUBLE sum;
-
 
727
#define TCROSSPROD_BODY					\
-
 
728
    R_xlen_t NRX = nrx, NRY = nry;			\
672
    for (int i = 0; i < nrx; i++)
729
    for (int i = 0; i < nrx; i++)			\
673
	for (int k = 0; k < nry; k++) {
730
	for (int k = 0; k < nry; k++) {			\
674
	    sum = 0.0;
731
	    sum = 0.0;					\
675
	    for (int j = 0; j < ncx; j++)
732
	    for (int j = 0; j < ncx; j++)		\
676
		sum += x[i + j * NRX] * y[k + j * NRY];
733
		sum += x[i + j * NRX] * y[k + j * NRY];	\
677
	    z[i + k * NRX] = (double) sum;
734
	    z[i + k * NRX] = (double) sum;		\
678
	}
735
	}
-
 
736
    TCROSSPROD_BODY;
-
 
737
}
-
 
738
 
-
 
739
static void simple_tcrossprod(double *x, int nrx, int ncx,
-
 
740
                              double *y, int nry, int ncy, double *z)
-
 
741
{
-
 
742
    double sum;
-
 
743
    TCROSSPROD_BODY;
679
}
744
}
680
 
745
 
-
 
746
 
681
static void matprod(double *x, int nrx, int ncx,
747
static void matprod(double *x, int nrx, int ncx,
682
		    double *y, int nry, int ncy, double *z)
748
		    double *y, int nry, int ncy, double *z)
683
{
749
{
684
    char *transN = "N", *transT = "T";
-
 
685
    double one = 1.0, zero = 0.0;
-
 
686
    int ione = 1;
-
 
687
    R_xlen_t NRX = nrx, NRY = nry;
750
    R_xlen_t NRX = nrx, NRY = nry;
-
 
751
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
-
 
752
	/* zero-extent operations should return zeroes */
-
 
753
	for(R_xlen_t i = 0; i < NRX*ncy; i++) z[i] = 0;
-
 
754
	return;
-
 
755
    }
688
 
756
 
689
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
757
    switch(R_Matprod) {
-
 
758
	case MATPROD_DEFAULT:
690
	/* Don't trust the BLAS to handle NA/NaNs correctly: PR#4582
759
	/* Don't trust the BLAS to handle NA/NaNs correctly: PR#4582
691
	 * The test is only O(n) here.
760
	 * The test is only O(n) here.
692
	 *
761
	 *
693
	 * MKL disclaimer: "LAPACK routines assume that input matrices
762
	 * MKL disclaimer: "LAPACK routines assume that input matrices
694
	 * do not contain IEEE 754 special values such as INF or NaN values.
763
	 * do not contain IEEE 754 special values such as INF or NaN values.
695
	 * Using these special values may cause LAPACK to return unexpected
764
	 * Using these special values may cause LAPACK to return unexpected
696
	 * results or become unstable."
765
	 * results or become unstable."
697
	 */
766
	 */
698
	if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy))
767
	    if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy)) {
699
	    simple_matprod(x, nrx, ncx, y, nry, ncy, z);
768
		simple_matprod(x, nrx, ncx, y, nry, ncy, z);
-
 
769
		return;
-
 
770
	    }
-
 
771
	    break; /* use blas */
-
 
772
	case MATPROD_INTERNAL:
-
 
773
	    internal_matprod(x, nrx, ncx, y, nry, ncy, z);
-
 
774
	    return;
-
 
775
	case MATPROD_BLAS:
-
 
776
	    break;
-
 
777
	case MATPROD_DEFAULT_SIMD:
-
 
778
	    if (mayHaveNaNOrInf_simd(x, NRX*ncx) ||
-
 
779
		    mayHaveNaNOrInf_simd(y, NRY*ncy)) {
-
 
780
		simple_matprod(x, nrx, ncx, y, nry, ncy, z);
-
 
781
		return;
-
 
782
	    }
-
 
783
	    break; /* use blas */
-
 
784
    }
-
 
785
 
-
 
786
    char *transN = "N", *transT = "T";
-
 
787
    double one = 1.0, zero = 0.0;
-
 
788
    int ione = 1;
-
 
789
 
700
	else if (ncy == 1) /* matrix-vector or dot product */
790
    if (ncy == 1) /* matrix-vector or dot product */
701
	    F77_CALL(dgemv)(transN, &nrx, &ncx, &one, x,
791
	F77_CALL(dgemv)(transN, &nrx, &ncx, &one, x,
702
	                    &nrx, y, &ione, &zero, z, &ione);
792
			&nrx, y, &ione, &zero, z, &ione);
703
	else if (nrx == 1) /* vector-matrix */
793
    else if (nrx == 1) /* vector-matrix */
704
	    /* Instead of xY, compute (xY)^T == (Y^T)(x^T)
794
	/* Instead of xY, compute (xY)^T == (Y^T)(x^T)
705
	       The result is a vector, so transposing its content is no-op */
795
	   The result is a vector, so transposing its content is no-op */
706
	    F77_CALL(dgemv)(transT, &nry, &ncy, &one, y,
796
	F77_CALL(dgemv)(transT, &nry, &ncy, &one, y,
707
	                    &nry, x, &ione, &zero, z, &ione);
797
			&nry, x, &ione, &zero, z, &ione);
708
	else /* matrix-matrix or outer product */
798
    else /* matrix-matrix or outer product */
709
	    F77_CALL(dgemm)(transN, transN, &nrx, &ncy, &ncx, &one,
799
	F77_CALL(dgemm)(transN, transN, &nrx, &ncy, &ncx, &one,
710
			    x, &nrx, y, &nry, &zero, z, &nrx);
800
			x, &nrx, y, &nry, &zero, z, &nrx);
711
    } else /* zero-extent operations should return zeroes */
-
 
712
	for(R_xlen_t i = 0; i < NRX*ncy; i++) z[i] = 0;
-
 
713
}
801
}
714
 
802
 
715
static R_INLINE
-
 
716
void simple_cmatprod(Rcomplex *x, int nrx, int ncx,
803
static void internal_cmatprod(Rcomplex *x, int nrx, int ncx,
717
		     Rcomplex *y, int nry, int ncy, Rcomplex *z)
804
                              Rcomplex *y, int nry, int ncy, Rcomplex *z)
718
{
805
{
719
    int i, j, k;
-
 
720
    double xij_r, xij_i, yjk_r, yjk_i;
-
 
721
    LDOUBLE sum_i, sum_r;
806
    LDOUBLE sum_i, sum_r;
722
 
-
 
-
 
807
#define CMATPROD_BODY					    \
-
 
808
    int i, j, k;					    \
-
 
809
    double complex xij, yjk;				    \
723
    R_xlen_t NRX = nrx, NRY = nry;
810
    R_xlen_t NRX = nrx, NRY = nry;			    \
724
    for (i = 0; i < nrx; i++)
811
    for (i = 0; i < nrx; i++)				    \
725
	for (k = 0; k < ncy; k++) {
812
	for (k = 0; k < ncy; k++) {			    \
726
	    sum_r = 0.0;
813
	    sum_r = 0.0;				    \
727
	    sum_i = 0.0;
814
	    sum_i = 0.0;				    \
728
	    for (j = 0; j < ncx; j++) {
815
	    for (j = 0; j < ncx; j++) {			    \
729
		xij_r = x[i + j * NRX].r;
-
 
730
		xij_i = x[i + j * NRX].i;
816
		xij = toC99(x + (i + j * NRX));		    \
731
		yjk_r = y[j + k * NRY].r;
-
 
732
		yjk_i = y[j + k * NRY].i;
817
		yjk = toC99(y + (j + k * NRY));		    \
733
		sum_r += (xij_r * yjk_r - xij_i * yjk_i);
818
		sum_r += creal(xij * yjk);		    \
734
		sum_i += (xij_r * yjk_i + xij_i * yjk_r);
819
		sum_i += cimag(xij * yjk);		    \
735
	    }
820
	    }						    \
736
	    z[i + k * NRX].r = (double) sum_r;
821
	    z[i + k * NRX].r = (double) sum_r;		    \
737
	    z[i + k * NRX].i = (double) sum_i;
822
	    z[i + k * NRX].i = (double) sum_i;		    \
738
	}
823
	}
-
 
824
    CMATPROD_BODY;
739
}
825
}
740
 
826
 
741
static R_INLINE
-
 
742
void simple_ccrossprod(Rcomplex *x, int nrx, int ncx,
827
static void simple_cmatprod(Rcomplex *x, int nrx, int ncx,
743
		       Rcomplex *y, int nry, int ncy, Rcomplex *z)
828
                            Rcomplex *y, int nry, int ncy, Rcomplex *z)
744
{
829
{
745
    int i, j, k;
-
 
746
    double xji_r, xji_i, yjk_r, yjk_i;
830
    double sum_i, sum_r;
747
    LDOUBLE sum_i, sum_r;
831
    CMATPROD_BODY;
-
 
832
}
748
 
833
 
-
 
834
static void internal_ccrossprod(Rcomplex *x, int nrx, int ncx,
-
 
835
                                Rcomplex *y, int nry, int ncy, Rcomplex *z)
-
 
836
{
-
 
837
    LDOUBLE sum_i, sum_r;
-
 
838
#define CCROSSPROD_BODY					    \
-
 
839
    int i, j, k;					    \
-
 
840
    double complex xji, yjk;				    \
749
    R_xlen_t NRX = nrx, NRY = nry, NCX = ncx;
841
    R_xlen_t NRX = nrx, NRY = nry, NCX = ncx;		    \
750
    for (i = 0; i < ncx; i++)
842
    for (i = 0; i < ncx; i++)				    \
751
	for (k = 0; k < ncy; k++) {
843
	for (k = 0; k < ncy; k++) {			    \
752
	    sum_r = 0.0;
844
	    sum_r = 0.0;				    \
753
	    sum_i = 0.0;
845
	    sum_i = 0.0;				    \
754
	    for (j = 0; j < nrx; j++) {
846
	    for (j = 0; j < nrx; j++) {			    \
755
		xji_r = x[j + i * NRX].r;
-
 
756
		xji_i = x[j + i * NRX].i;
847
		xji = toC99(x + (j + i * NRX));		    \
757
		yjk_r = y[j + k * NRY].r;
-
 
758
		yjk_i = y[j + k * NRY].i;
848
		yjk = toC99(y + (j + k * NRY));		    \
759
		sum_r += (xji_r * yjk_r - xji_i * yjk_i);
849
		sum_r += creal(xji * yjk);		    \
760
		sum_i += (xji_r * yjk_i + xji_i * yjk_r);
850
		sum_i += cimag(xji * yjk);		    \
761
	    }
851
	    }						    \
762
	    z[i + k * NCX].r = (double) sum_r;
852
	    z[i + k * NCX].r = (double) sum_r;		    \
763
	    z[i + k * NCX].i = (double) sum_i;
853
	    z[i + k * NCX].i = (double) sum_i;		    \
764
	}
854
	}
-
 
855
    CCROSSPROD_BODY;
765
}
856
}
766
 
857
 
767
static R_INLINE
-
 
768
void simple_tccrossprod(Rcomplex *x, int nrx, int ncx,
858
static void simple_ccrossprod(Rcomplex *x, int nrx, int ncx,
769
		        Rcomplex *y, int nry, int ncy, Rcomplex *z)
859
                              Rcomplex *y, int nry, int ncy, Rcomplex *z)
770
{
860
{
771
    int i, j, k;
-
 
772
    double xij_r, xij_i, ykj_r, ykj_i;
861
    double sum_i, sum_r;
773
    LDOUBLE sum_i, sum_r;
862
    CCROSSPROD_BODY;
-
 
863
}
774
 
864
 
-
 
865
static void internal_tccrossprod(Rcomplex *x, int nrx, int ncx,
-
 
866
                                 Rcomplex *y, int nry, int ncy, Rcomplex *z)
-
 
867
{
-
 
868
    LDOUBLE sum_i, sum_r;
-
 
869
#define TCCROSSPROD_BODY				    \
-
 
870
    int i, j, k;					    \
-
 
871
    double complex xij, ykj;				    \
775
    R_xlen_t NRX = nrx, NRY = nry;
872
    R_xlen_t NRX = nrx, NRY = nry;			    \
776
    for (i = 0; i < nrx; i++)
873
    for (i = 0; i < nrx; i++)				    \
777
	for (k = 0; k < nry; k++) {
874
	for (k = 0; k < nry; k++) {			    \
778
	    sum_r = 0.0;
875
	    sum_r = 0.0;				    \
779
	    sum_i = 0.0;
876
	    sum_i = 0.0;				    \
780
	    for (j = 0; j < ncx; j++) {
877
	    for (j = 0; j < ncx; j++) {			    \
781
		xij_r = x[i + j * NRX].r;
-
 
782
		xij_i = x[i + j * NRX].i;
878
		xij = toC99(x + (i + j * NRX));		    \
783
		ykj_r = y[k + j * NRY].r;
-
 
784
		ykj_i = y[k + j * NRY].i;
879
		ykj = toC99(y + (k + j * NRY));		    \
785
		sum_r += (xij_r * ykj_r - xij_i * ykj_i);
880
		sum_r += creal(xij * ykj);		    \
786
		sum_i += (xij_r * ykj_i + xij_i * ykj_r);
881
		sum_i += cimag(xij * ykj);		    \
787
	    }
882
	    }						    \
788
	    z[i + k * NRX].r = (double) sum_r;
883
	    z[i + k * NRX].r = (double) sum_r;		    \
789
	    z[i + k * NRX].i = (double) sum_i;
884
	    z[i + k * NRX].i = (double) sum_i;		    \
790
	}
885
	}
-
 
886
    TCCROSSPROD_BODY;
-
 
887
}
-
 
888
 
-
 
889
static void simple_tccrossprod(Rcomplex *x, int nrx, int ncx,
-
 
890
                               Rcomplex *y, int nry, int ncy, Rcomplex *z)
-
 
891
{
-
 
892
    double sum_i, sum_r;
-
 
893
    TCCROSSPROD_BODY;
791
}
894
}
792
 
895
 
793
static void cmatprod(Rcomplex *x, int nrx, int ncx,
896
static void cmatprod(Rcomplex *x, int nrx, int ncx,
794
		     Rcomplex *y, int nry, int ncy, Rcomplex *z)
897
		     Rcomplex *y, int nry, int ncy, Rcomplex *z)
795
{
898
{
796
    char *transa = "N", *transb = "N";
-
 
797
    Rcomplex one, zero;
-
 
798
    R_xlen_t NRX = nrx, NRY = nry;
899
    R_xlen_t NRX = nrx, NRY = nry;
-
 
900
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
-
 
901
	/* zero-extent operations should return zeroes */
-
 
902
	for(R_xlen_t i = 0; i < NRX*ncy; i++) z[i].r = z[i].i = 0;
-
 
903
	return;
-
 
904
    }
799
 
905
 
800
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
801
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
-
 
802
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
906
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
-
 
907
    if (R_Matprod == MATPROD_INTERNAL)
-
 
908
	internal_cmatprod(x, nrx, ncx, y, nry, ncy, z);
-
 
909
    else
803
	simple_cmatprod(x, nrx, ncx, y, nry, ncy, z);
910
	simple_cmatprod(x, nrx, ncx, y, nry, ncy, z);
804
#else
911
#else
-
 
912
    switch(R_Matprod) {
-
 
913
	case MATPROD_DEFAULT:
805
	if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy))
914
	    if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy)) {
-
 
915
		simple_cmatprod(x, nrx, ncx, y, nry, ncy, z);
-
 
916
		return;
-
 
917
	    }
-
 
918
	    break; /* use blas */
-
 
919
	case MATPROD_INTERNAL:
-
 
920
	    internal_cmatprod(x, nrx, ncx, y, nry, ncy, z);
-
 
921
	    return;
-
 
922
	case MATPROD_BLAS:
-
 
923
	    break;
-
 
924
	case MATPROD_DEFAULT_SIMD:
-
 
925
	    if (cmayHaveNaNOrInf_simd(x, NRX*ncx) ||
-
 
926
		    cmayHaveNaNOrInf_simd(y, NRY*ncy)) {
806
	    simple_cmatprod(x, nrx, ncx, y, nry, ncy, z);
927
		simple_cmatprod(x, nrx, ncx, y, nry, ncy, z);
-
 
928
		return;
807
	else
929
	    }
-
 
930
	    break; /* use blas */
-
 
931
    }
-
 
932
 
-
 
933
    char *transa = "N", *transb = "N";
-
 
934
    Rcomplex one, zero;
-
 
935
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
936
 
808
	    F77_CALL(zgemm)(transa, transb, &nrx, &ncy, &ncx, &one,
937
    F77_CALL(zgemm)(transa, transb, &nrx, &ncy, &ncx, &one,
809
	                    x, &nrx, y, &nry, &zero, z, &nrx);
938
                    x, &nrx, y, &nry, &zero, z, &nrx);
810
#endif
939
#endif
811
    } else /* zero-extent operations should return zeroes */
-
 
812
	for(R_xlen_t i = 0; i < NRX*ncy; i++) z[i].r = z[i].i = 0;
-
 
813
}
940
}
814
 
941
 
815
static void symcrossprod(double *x, int nr, int nc, double *z)
942
static void symcrossprod(double *x, int nr, int nc, double *z)
816
{
943
{
817
    char *trans = "T", *uplo = "U";
-
 
818
    double one = 1.0, zero = 0.0;
-
 
819
    R_xlen_t NC = nc;
944
    R_xlen_t NR = nr, NC = nc;
820
    if (nr > 0 && nc > 0) {
945
    if (nr == 0 || nc == 0) {
821
	F77_CALL(dsyrk)(uplo, trans, &nc, &nr, &one, x, &nr, &zero, z, &nc);
-
 
822
	for (int i = 1; i < nc; i++)
-
 
823
	    for (int j = 0; j < i; j++) z[i + NC *j] = z[j + NC * i];
-
 
824
    } else { /* zero-extent operations should return zeroes */
946
	/* zero-extent operations should return zeroes */
825
	for(R_xlen_t i = 0; i < NC*NC; i++) z[i] = 0;
947
	for(R_xlen_t i = 0; i < NC*NC; i++) z[i] = 0;
-
 
948
	return;
-
 
949
    }
-
 
950
 
-
 
951
    switch(R_Matprod) {
-
 
952
	case MATPROD_DEFAULT:
-
 
953
	    /* see matprod for more details */
-
 
954
	    if (mayHaveNaNOrInf(x, NR*nc)) {
-
 
955
		simple_crossprod(x, nr, nc, x, nr, nc, z);
-
 
956
		return;
-
 
957
	    }
-
 
958
	    break; /* use blas */
-
 
959
	case MATPROD_INTERNAL:
-
 
960
	    internal_crossprod(x, nr, nc, x, nr, nc, z);
-
 
961
	    return;
-
 
962
	case MATPROD_BLAS:
-
 
963
	    break;
-
 
964
	case MATPROD_DEFAULT_SIMD:
-
 
965
	    if (mayHaveNaNOrInf_simd(x, NR*nc))  {
-
 
966
		simple_crossprod(x, nr, nc, x, nr, nc, z);
-
 
967
		return;
-
 
968
	    }
-
 
969
	    break; /* use blas */
826
    }
970
    }
827
 
971
 
-
 
972
    char *trans = "T", *uplo = "U";
-
 
973
    double one = 1.0, zero = 0.0;
-
 
974
 
-
 
975
    F77_CALL(dsyrk)(uplo, trans, &nc, &nr, &one, x, &nr, &zero, z, &nc);
-
 
976
    for (int i = 1; i < nc; i++)
-
 
977
	for (int j = 0; j < i; j++) z[i + NC *j] = z[j + NC * i];
828
}
978
}
829
 
979
 
830
static void crossprod(double *x, int nrx, int ncx,
980
static void crossprod(double *x, int nrx, int ncx,
831
		      double *y, int nry, int ncy, double *z)
981
		      double *y, int nry, int ncy, double *z)
832
{
982
{
833
    char *transT = "T", *transN = "N";
-
 
834
    double one = 1.0, zero = 0.0;
-
 
835
    int ione = 1;
-
 
836
    R_xlen_t NRX = nrx, NRY = nry;
983
    R_xlen_t NRX = nrx, NRY = nry;
837
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
984
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
838
	if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy))
-
 
839
	    /* see matprod for more details */
-
 
840
	    simple_crossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
841
	else if (ncy == 1) /* matrix-vector or dot product */
-
 
842
	    F77_CALL(dgemv)(transT, &nrx, &ncx, &one, x,
-
 
843
	                    &nrx, y, &ione, &zero, z, &ione);
-
 
844
	else if (ncx == 1) /* vector-matrix */
-
 
845
	    /* Instead of (x^T)Y, compute ((x^T)Y)^T == (Y^T)x
-
 
846
	       The result is a vector, so transposing its content is no-op */
-
 
847
	    F77_CALL(dgemv)(transT, &nry, &ncy, &one, y,
-
 
848
	                    &nry, x, &ione, &zero, z, &ione);
-
 
849
	else /* matrix-matrix  or outer product */
-
 
850
	    F77_CALL(dgemm)(transT, transN, &ncx, &ncy, &nrx, &one,
-
 
851
			x, &nrx, y, &nry, &zero, z, &ncx);
-
 
852
    } else { /* zero-extent operations should return zeroes */
985
	/* zero-extent operations should return zeroes */
853
	R_xlen_t NCX = ncx;
986
	R_xlen_t NCX = ncx;
854
	for(R_xlen_t i = 0; i < NCX*ncy; i++) z[i] = 0;
987
	for(R_xlen_t i = 0; i < NCX*ncy; i++) z[i] = 0;
-
 
988
	return;
855
    }
989
    }
-
 
990
 
-
 
991
    switch(R_Matprod) {
-
 
992
	case MATPROD_DEFAULT:
-
 
993
	    /* see matprod for more details */
-
 
994
	    if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy)) {
-
 
995
		simple_crossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
996
		return;
-
 
997
	    }
-
 
998
	    break; /* use blas */
-
 
999
	case MATPROD_INTERNAL:
-
 
1000
	    internal_crossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1001
	    return;
-
 
1002
	case MATPROD_BLAS:
-
 
1003
	    break;
-
 
1004
	case MATPROD_DEFAULT_SIMD:
-
 
1005
	    if (mayHaveNaNOrInf_simd(x, NRX*ncx) ||
-
 
1006
		    mayHaveNaNOrInf_simd(y, NRY*ncy)) {
-
 
1007
		simple_crossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1008
		return;
-
 
1009
	    }
-
 
1010
	    break; /* use blas */
-
 
1011
    }
-
 
1012
 
-
 
1013
    char *transT = "T", *transN = "N";
-
 
1014
    double one = 1.0, zero = 0.0;
-
 
1015
    int ione = 1;
-
 
1016
 
-
 
1017
    if (ncy == 1) /* matrix-vector or dot product */
-
 
1018
	F77_CALL(dgemv)(transT, &nrx, &ncx, &one, x,
-
 
1019
			&nrx, y, &ione, &zero, z, &ione);
-
 
1020
    else if (ncx == 1) /* vector-matrix */
-
 
1021
	/* Instead of (x^T)Y, compute ((x^T)Y)^T == (Y^T)x
-
 
1022
	   The result is a vector, so transposing its content is no-op */
-
 
1023
	F77_CALL(dgemv)(transT, &nry, &ncy, &one, y,
-
 
1024
			&nry, x, &ione, &zero, z, &ione);
-
 
1025
    else /* matrix-matrix  or outer product */
-
 
1026
	F77_CALL(dgemm)(transT, transN, &ncx, &ncy, &nrx, &one,
-
 
1027
		        x, &nrx, y, &nry, &zero, z, &ncx);
856
}
1028
}
857
 
1029
 
858
static void ccrossprod(Rcomplex *x, int nrx, int ncx,
1030
static void ccrossprod(Rcomplex *x, int nrx, int ncx,
859
		       Rcomplex *y, int nry, int ncy, Rcomplex *z)
1031
		       Rcomplex *y, int nry, int ncy, Rcomplex *z)
860
{
1032
{
861
    char *transa = "T", *transb = "N";
-
 
862
    Rcomplex one, zero;
-
 
863
    R_xlen_t NRX = nrx, NRY = nry;
1033
    R_xlen_t NRX = nrx, NRY = nry;
-
 
1034
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
-
 
1035
	/* zero-extent operations should return zeroes */
-
 
1036
	R_xlen_t NCX = ncx;
-
 
1037
	for(R_xlen_t i = 0; i < NCX*ncy; i++) z[i].r = z[i].i = 0;
-
 
1038
	return;
-
 
1039
    }
864
 
1040
 
865
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
866
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
-
 
867
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
1041
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
-
 
1042
    if (R_Matprod == MATPROD_INTERNAL)
-
 
1043
	internal_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1044
    else
868
	simple_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
1045
	simple_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
869
#else
1046
#else
-
 
1047
    switch(R_Matprod) {
-
 
1048
	case MATPROD_DEFAULT:
870
	if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy))
1049
	    if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy)) {
871
	    simple_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
1050
		simple_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1051
		return;
872
	else
1052
	    }
873
	    F77_CALL(zgemm)(transa, transb, &ncx, &ncy, &nrx, &one,
1053
	    break; /* use blas */
-
 
1054
	case MATPROD_INTERNAL:
874
	                    x, &nrx, y, &nry, &zero, z, &ncx);
1055
	    internal_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
875
#endif
1056
	    return;
-
 
1057
	case MATPROD_BLAS:
-
 
1058
	    break;
-
 
1059
	case MATPROD_DEFAULT_SIMD:
876
    } else { /* zero-extent operations should return zeroes */
1060
	    if (cmayHaveNaNOrInf_simd(x, NRX*ncx) ||
877
	R_xlen_t NCX = ncx;
1061
		    cmayHaveNaNOrInf_simd(y, NRY*ncy)) {
878
	for(R_xlen_t i = 0; i < NCX*ncy; i++) z[i].r = z[i].i = 0;
1062
		simple_ccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1063
		return;
-
 
1064
	    }
-
 
1065
	    break; /* use blas */
879
    }
1066
    }
-
 
1067
 
-
 
1068
    char *transa = "T", *transb = "N";
-
 
1069
    Rcomplex one, zero;
-
 
1070
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
1071
 
-
 
1072
    F77_CALL(zgemm)(transa, transb, &ncx, &ncy, &nrx, &one,
-
 
1073
                    x, &nrx, y, &nry, &zero, z, &ncx);
-
 
1074
#endif
880
}
1075
}
881
 
1076
 
882
static void symtcrossprod(double *x, int nr, int nc, double *z)
1077
static void symtcrossprod(double *x, int nr, int nc, double *z)
883
{
1078
{
884
    char *trans = "N", *uplo = "U";
-
 
885
    double one = 1.0, zero = 0.0;
1079
    R_xlen_t NR = nr;
886
    if (nr > 0 && nc > 0) {
1080
    if (nr == 0 || nc == 0) {
887
	F77_CALL(dsyrk)(uplo, trans, &nr, &nc, &one, x, &nr, &zero, z, &nr);
-
 
888
	for (int i = 1; i < nr; i++)
-
 
889
	    for (int j = 0; j < i; j++) z[i + nr *j] = z[j + nr * i];
-
 
890
    } else { /* zero-extent operations should return zeroes */
1081
	/* zero-extent operations should return zeroes */
891
	R_xlen_t NR = nr;
-
 
892
	for(R_xlen_t i = 0; i < NR*NR; i++) z[i] = 0;
1082
	for(R_xlen_t i = 0; i < NR*NR; i++) z[i] = 0;
-
 
1083
	return;
893
    }
1084
    }
894
 
1085
 
-
 
1086
    switch(R_Matprod) {
-
 
1087
	case MATPROD_DEFAULT:
-
 
1088
	    /* see matprod for more details */
-
 
1089
	    if (mayHaveNaNOrInf(x, NR*nc)) {
-
 
1090
		simple_tcrossprod(x, nr, nc, x, nr, nc, z);
-
 
1091
		return;
-
 
1092
	    }
-
 
1093
	    break; /* use blas */
-
 
1094
	case MATPROD_INTERNAL:
-
 
1095
	    internal_tcrossprod(x, nr, nc, x, nr, nc, z);
-
 
1096
	    return;
-
 
1097
	case MATPROD_BLAS:
-
 
1098
	    break;
-
 
1099
	case MATPROD_DEFAULT_SIMD:
-
 
1100
	    if (mayHaveNaNOrInf_simd(x, NR*nc))  {
-
 
1101
		simple_tcrossprod(x, nr, nc, x, nr, nc, z);
-
 
1102
		return;
-
 
1103
	    }
-
 
1104
	    break; /* use blas */
-
 
1105
    }
-
 
1106
 
-
 
1107
    char *trans = "N", *uplo = "U";
-
 
1108
    double one = 1.0, zero = 0.0;
-
 
1109
 
-
 
1110
    F77_CALL(dsyrk)(uplo, trans, &nr, &nc, &one, x, &nr, &zero, z, &nr);
-
 
1111
    for (int i = 1; i < nr; i++)
-
 
1112
	for (int j = 0; j < i; j++) z[i + nr *j] = z[j + nr * i];
895
}
1113
}
896
 
1114
 
897
static void tcrossprod(double *x, int nrx, int ncx,
1115
static void tcrossprod(double *x, int nrx, int ncx,
898
		      double *y, int nry, int ncy, double *z)
1116
		      double *y, int nry, int ncy, double *z)
899
{
1117
{
900
    char *transN = "N", *transT = "T";
-
 
901
    double one = 1.0, zero = 0.0;
-
 
902
    int ione = 1;
-
 
903
    R_xlen_t NRX = nrx, NRY = nry;
1118
    R_xlen_t NRX = nrx, NRY = nry;
904
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
1119
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
905
	if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy))
-
 
906
	    /* see matprod for more details */
-
 
907
	    simple_tcrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
908
	else if (nry == 1) /* matrix-vector or dot product */
-
 
909
	    F77_CALL(dgemv)(transN, &nrx, &ncx, &one, x,
-
 
910
	                    &nrx, y, &ione, &zero, z, &ione);
-
 
911
	else if (nrx == 1) /* vector-matrix */
-
 
912
	    /* Instead of x(Y^T), compute (x(Y^T))^T == Y(x^T)
-
 
913
	       The result is a vector, so transposing its content is no-op */
-
 
914
	    F77_CALL(dgemv)(transN, &nry, &ncy, &one, y,
-
 
915
	                    &nry, x, &ione, &zero, z, &ione);
-
 
916
	else /* matrix-matrix or outer product */
-
 
917
	    F77_CALL(dgemm)(transN, transT, &nrx, &nry, &ncx, &one,
-
 
918
			x, &nrx, y, &nry, &zero, z, &nrx);
-
 
919
    } else { /* zero-extent operations should return zeroes */
1120
	/* zero-extent operations should return zeroes */
920
	R_xlen_t NRX = nrx;
-
 
921
	for(R_xlen_t i = 0; i < NRX*nry; i++) z[i] = 0;
1121
	for(R_xlen_t i = 0; i < NRX*nry; i++) z[i] = 0;
-
 
1122
	return;
-
 
1123
    }
-
 
1124
 
-
 
1125
    switch(R_Matprod) {
-
 
1126
	case MATPROD_DEFAULT:
-
 
1127
	    if (mayHaveNaNOrInf(x, NRX*ncx) || mayHaveNaNOrInf(y, NRY*ncy)) {
-
 
1128
		simple_tcrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1129
		return;
-
 
1130
	    }
-
 
1131
	    break; /* use blas */
-
 
1132
	case MATPROD_INTERNAL:
-
 
1133
	    internal_tcrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1134
	    return;
-
 
1135
	case MATPROD_BLAS:
-
 
1136
	    break;
-
 
1137
	case MATPROD_DEFAULT_SIMD:
-
 
1138
	    if (mayHaveNaNOrInf_simd(x, NRX*ncx) ||
-
 
1139
		    mayHaveNaNOrInf_simd(y, NRY*ncy)) {
-
 
1140
		simple_tcrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1141
		return;
-
 
1142
	    }
-
 
1143
	    break; /* use blas */
922
    }
1144
    }
-
 
1145
 
-
 
1146
    char *transN = "N", *transT = "T";
-
 
1147
    double one = 1.0, zero = 0.0;
-
 
1148
    int ione = 1;
-
 
1149
 
-
 
1150
    if (nry == 1) /* matrix-vector or dot product */
-
 
1151
	F77_CALL(dgemv)(transN, &nrx, &ncx, &one, x,
-
 
1152
			&nrx, y, &ione, &zero, z, &ione);
-
 
1153
    else if (nrx == 1) /* vector-matrix */
-
 
1154
	/* Instead of x(Y^T), compute (x(Y^T))^T == Y(x^T)
-
 
1155
	   The result is a vector, so transposing its content is no-op */
-
 
1156
	F77_CALL(dgemv)(transN, &nry, &ncy, &one, y,
-
 
1157
			&nry, x, &ione, &zero, z, &ione);
-
 
1158
    else /* matrix-matrix or outer product */
-
 
1159
	F77_CALL(dgemm)(transN, transT, &nrx, &nry, &ncx, &one,
-
 
1160
		    x, &nrx, y, &nry, &zero, z, &nrx);
923
}
1161
}
924
 
1162
 
925
static void tccrossprod(Rcomplex *x, int nrx, int ncx,
1163
static void tccrossprod(Rcomplex *x, int nrx, int ncx,
926
			Rcomplex *y, int nry, int ncy, Rcomplex *z)
1164
			Rcomplex *y, int nry, int ncy, Rcomplex *z)
927
{
1165
{
928
    char *transa = "N", *transb = "T";
-
 
929
    Rcomplex one, zero;
-
 
930
    R_xlen_t NRX = nrx, NRY = nry;
1166
    R_xlen_t NRX = nrx, NRY = nry;
-
 
1167
    if (nrx == 0 || ncx == 0 || nry == 0 || ncy == 0) {
-
 
1168
	/* zero-extent operations should return zeroes */
-
 
1169
	for(R_xlen_t i = 0; i < NRX*nry; i++) z[i].r = z[i].i = 0;
-
 
1170
	return;
-
 
1171
    }
931
 
1172
 
932
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
933
    if (nrx > 0 && ncx > 0 && nry > 0 && ncy > 0) {
-
 
934
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
1173
#ifndef HAVE_FORTRAN_DOUBLE_COMPLEX
-
 
1174
    if (R_Matprod == MATPROD_INTERNAL)
-
 
1175
	internal_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1176
    else
935
	simple_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
1177
	simple_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
936
#else
1178
#else
-
 
1179
    switch(R_Matprod) {
-
 
1180
	case MATPROD_DEFAULT:
937
	if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy))
1181
	    if (cmayHaveNaNOrInf(x, NRX*ncx) || cmayHaveNaNOrInf(y, NRY*ncy)) {
938
	    simple_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
1182
		simple_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1183
		return;
939
	else
1184
	    }
940
	    F77_CALL(zgemm)(transa, transb, &nrx, &nry, &ncx, &one,
1185
	    break; /* use blas */
-
 
1186
	case MATPROD_INTERNAL:
941
	                    x, &nrx, y, &nry, &zero, z, &nrx);
1187
	    internal_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
942
#endif
1188
	    return;
-
 
1189
	case MATPROD_BLAS:
-
 
1190
	    break;
-
 
1191
	case MATPROD_DEFAULT_SIMD:
943
    } else { /* zero-extent operations should return zeroes */
1192
	    if (cmayHaveNaNOrInf_simd(x, NRX*ncx) ||
944
	R_xlen_t NRX = nrx;
1193
		    cmayHaveNaNOrInf_simd(y, NRY*ncy)) {
945
	for(R_xlen_t i = 0; i < NRX*nry; i++) z[i].r = z[i].i = 0;
1194
		simple_tccrossprod(x, nrx, ncx, y, nry, ncy, z);
-
 
1195
		return;
-
 
1196
	    }
-
 
1197
	    break; /* use blas */
946
    }
1198
    }
-
 
1199
 
-
 
1200
    char *transa = "N", *transb = "T";
-
 
1201
    Rcomplex one, zero;
-
 
1202
    one.r = 1.0; one.i = zero.r = zero.i = 0.0;
-
 
1203
 
-
 
1204
    F77_CALL(zgemm)(transa, transb, &nrx, &nry, &ncx, &one,
-
 
1205
                    x, &nrx, y, &nry, &zero, z, &nrx);
-
 
1206
#endif
947
}
1207
}
948
 
1208
 
949
 
1209
 
950
/* "%*%" (op = 0), crossprod (op = 1) or tcrossprod (op = 2) */
1210
/* "%*%" (op = 0), crossprod (op = 1) or tcrossprod (op = 2) */
951
SEXP attribute_hidden do_matprod(SEXP call, SEXP op, SEXP args, SEXP rho)
1211
SEXP attribute_hidden do_matprod(SEXP call, SEXP op, SEXP args, SEXP rho)