blob: 1dd243e7639b1ac874f7ec2a7731eda0f59010d7 [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 1995-2018 The R Core Team
* Copyright (C) 2003 The R Foundation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_LONG_DOUBLE
# define SQRTL sqrtl
#else
# define SQRTL sqrt
#endif
#include <Defn.h>
#include <Rmath.h>
#include "statsR.h"
#undef _
#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) dgettext ("stats", String)
#else
#define _(String) (String)
#endif
static SEXP corcov(SEXP x, SEXP y, SEXP na_method, SEXP kendall, Rboolean cor);
SEXP cor(SEXP x, SEXP y, SEXP na_method, SEXP kendall)
{
return corcov(x, y, na_method, kendall, TRUE);
}
SEXP cov(SEXP x, SEXP y, SEXP na_method, SEXP kendall)
{
return corcov(x, y, na_method, kendall, FALSE);
}
#define COV_SUM_UPDATE \
sum += xm * ym; \
if(cor) { \
xsd += xm * xm; \
ysd += ym * ym; \
}
#define ANS(I,J) ans[I + J * ncx]
#define CLAMP(X) (X >= 1. ? 1. : (X <= -1. ? -1. : X))
/* Note that "if (kendall)" and "if (cor)" are used inside a double for() loop;
which makes the code better readable -- and is hopefully dealt with
by a smartly optimizing compiler
*/
/** Compute Cov(xx[], yy[]) or Cor(.,.) with n = length(xx)
*/
#define COV_PAIRWISE_BODY \
LDOUBLE sum, xmean = 0., ymean = 0., xsd, ysd, xm, ym; \
int k, nobs, n1 = -1; /* -Wall initializing */ \
\
nobs = 0; \
if(!kendall) { \
xmean = ymean = 0.; \
for (k = 0 ; k < n ; k++) { \
if(!(ISNAN(xx[k]) || ISNAN(yy[k]))) { \
nobs ++; \
xmean += xx[k]; \
ymean += yy[k]; \
} \
} \
} else /*kendall*/ \
for (k = 0 ; k < n ; k++) \
if(!(ISNAN(xx[k]) || ISNAN(yy[k]))) \
nobs ++; \
\
if (nobs >= 2) { \
xsd = ysd = sum = 0.; \
if(!kendall) { \
xmean /= nobs; \
ymean /= nobs; \
n1 = nobs-1; \
} \
for(k=0; k < n; k++) { \
if(!(ISNAN(xx[k]) || ISNAN(yy[k]))) { \
if(!kendall) { \
xm = xx[k] - xmean; \
ym = yy[k] - ymean; \
\
COV_SUM_UPDATE \
} \
else { /* Kendall's tau */ \
for(n1=0 ; n1 < k ; n1++) \
if(!(ISNAN(xx[n1]) || ISNAN(yy[n1]))) { \
xm = sign(xx[k] - xx[n1]); \
ym = sign(yy[k] - yy[n1]); \
\
COV_SUM_UPDATE \
} \
} \
} \
} \
if (cor) { \
if(xsd == 0. || ysd == 0.) { \
*sd_0 = TRUE; \
sum = NA_REAL; \
} \
else { \
if(!kendall) { \
xsd /= n1; \
ysd /= n1; \
sum /= n1; \
} \
sum /= (SQRTL(xsd) * SQRTL(ysd)); \
sum = CLAMP(sum); \
} \
} \
else if(!kendall) \
sum /= n1; \
\
ANS(i,j) = (double) sum; \
} \
else \
ANS(i,j) = NA_REAL
static void cov_pairwise1(int n, int ncx, double *x,
double *ans, Rboolean *sd_0, Rboolean cor,
Rboolean kendall)
{
for (int i = 0 ; i < ncx ; i++) {
double *xx = &x[i * n];
for (int j = 0 ; j <= i ; j++) {
double *yy = &x[j * n];
COV_PAIRWISE_BODY;
ANS(j,i) = ANS(i,j);
}
}
}
static void cov_pairwise2(int n, int ncx, int ncy, double *x, double *y,
double *ans, Rboolean *sd_0, Rboolean cor,
Rboolean kendall)
{
for (int i = 0 ; i < ncx ; i++) {
double *xx = &x[i * n];
for (int j = 0 ; j < ncy ; j++) {
double *yy = &y[j * n];
COV_PAIRWISE_BODY;
}
}
}
#undef COV_PAIRWISE_BODY
/* method = "complete" or "all.obs" (only difference: na_fail):
* -------- -------
*/
#define COV_ini_0 \
LDOUBLE sum, tmp, xxm, yym; \
double *xx, *yy; \
R_xlen_t i, j, k, n1=-1/* -Wall */
#define COV_n_le_1(_n_,_k_) \
if (_n_ <= 1) {/* too many missing */ \
for (i = 0 ; i < ncx ; i++) \
for (j = 0 ; j < _k_ ; j++) \
ANS(i,j) = NA_REAL; \
return; \
}
#define COV_init(_ny_) \
COV_ini_0; int nobs; \
\
/* total number of complete observations */ \
nobs = 0; \
for(k = 0 ; k < n ; k++) { \
if (ind[k] != 0) nobs++; \
} \
COV_n_le_1(nobs, _ny_)
#define COV_ini_na(_ny_) \
COV_ini_0; \
COV_n_le_1(n, _ny_)
/* This uses two passes for better accuracy */
#define MEAN(_X_) \
/* variable means */ \
for (i = 0 ; i < nc##_X_ ; i++) { \
xx = &_X_[i * n]; \
sum = 0.; \
for (k = 0 ; k < n ; k++) \
if(ind[k] != 0) \
sum += xx[k]; \
tmp = sum / nobs; \
if(R_FINITE((double)tmp)) { \
sum = 0.; \
for (k = 0 ; k < n ; k++) \
if(ind[k] != 0) \
sum += (xx[k] - tmp); \
tmp = tmp + sum / nobs; \
} \
_X_##m [i] = (double)tmp; \
}
/* This uses two passes for better accuracy */
#define MEAN_(_X_,_HAS_NA_) \
/* variable means (has_na) */ \
for (i = 0 ; i < nc##_X_ ; i++) { \
if(_HAS_NA_[i]) \
tmp = NA_REAL; \
else { \
xx = &_X_[i * n]; \
sum = 0.; \
for (k = 0 ; k < n ; k++) \
sum += xx[k]; \
tmp = sum / n; \
if(R_FINITE((double)tmp)) { \
sum = 0.; \
for (k = 0 ; k < n ; k++) \
sum += (xx[k] - tmp); \
tmp = tmp + sum / n; \
} \
} \
_X_##m [i] = (double)tmp; \
}
static void
cov_complete1(int n, int ncx, double *x, double *xm,
int *ind, double *ans, Rboolean *sd_0, Rboolean cor,
Rboolean kendall)
{
COV_init(ncx);
if(!kendall) {
MEAN(x);/* -> xm[] */
n1 = nobs - 1;
}
for (i = 0 ; i < ncx ; i++) {
xx = &x[i * n];
if(!kendall) {
xxm = xm[i];
for (j = 0 ; j <= i ; j++) {
yy = &x[j * n];
yym = xm[j];
sum = 0.;
for (k = 0 ; k < n ; k++)
if (ind[k] != 0)
sum += (LDOUBLE)(xx[k] - xxm) * (yy[k] - yym);
ANS(j,i) = ANS(i,j) = (double)(sum / n1);
}
}
else { /* Kendall's tau */
for (j = 0 ; j <= i ; j++) {
yy = &x[j * n];
sum = 0.;
for (k = 0 ; k < n ; k++)
if (ind[k] != 0)
for (n1 = 0 ; n1 < n ; n1++)
if (ind[n1] != 0)
sum += sign(xx[k] - xx[n1])
* sign(yy[k] - yy[n1]);
ANS(j,i) = ANS(i,j) = (double)sum;
}
}
}
if (cor) {
for (i = 0 ; i < ncx ; i++)
xm[i] = sqrt(ANS(i,i));
for (i = 0 ; i < ncx ; i++) {
for (j = 0 ; j < i ; j++) {
if (xm[i] == 0 || xm[j] == 0) {
*sd_0 = TRUE;
ANS(j,i) = ANS(i,j) = NA_REAL;
}
else {
sum = ANS(i,j) / (xm[i] * xm[j]);
ANS(j,i) = ANS(i,j) = (double)CLAMP(sum);
}
}
ANS(i,i) = 1.0;
}
}
} /* cov_complete1 */
static void
cov_na_1(int n, int ncx, double *x, double *xm,
int *has_na, double *ans, Rboolean *sd_0, Rboolean cor,
Rboolean kendall)
{
COV_ini_na(ncx);
if(!kendall) {
MEAN_(x, has_na);/* -> xm[] */
n1 = n - 1;
}
for (i = 0 ; i < ncx ; i++) {
if(has_na[i]) {
for (j = 0 ; j <= i ; j++)
ANS(j,i) = ANS(i,j) = NA_REAL;
}
else {
xx = &x[i * n];
if(!kendall) {
xxm = xm[i];
for (j = 0 ; j <= i ; j++)
if(has_na[j]) {
ANS(j,i) = ANS(i,j) = NA_REAL;
} else {
yy = &x[j * n];
yym = xm[j];
sum = 0.;
for (k = 0 ; k < n ; k++)
sum += (LDOUBLE)(xx[k] - xxm) * (yy[k] - yym);
ANS(j,i) = ANS(i,j) = (double)(sum / n1);
}
}
else { /* Kendall's tau */
for (j = 0 ; j <= i ; j++)
if(has_na[j]) {
ANS(j,i) = ANS(i,j) = NA_REAL;
} else {
yy = &x[j * n];
sum = 0.;
for (k = 0 ; k < n ; k++)
for (n1 = 0 ; n1 < n ; n1++)
sum += sign(xx[k] - xx[n1]) * sign(yy[k] - yy[n1]);
ANS(j,i) = ANS(i,j) = (double)sum;
}
}
}
}
if (cor) {
for (i = 0 ; i < ncx ; i++)
if(!has_na[i]) xm[i] = sqrt(ANS(i,i));
for (i = 0 ; i < ncx ; i++) {
if(!has_na[i]) for (j = 0 ; j < i ; j++) {
if (xm[i] == 0 || xm[j] == 0) {
*sd_0 = TRUE;
ANS(j,i) = ANS(i,j) = NA_REAL;
}
else {
sum = ANS(i,j) / (xm[i] * xm[j]);
ANS(j,i) = ANS(i,j) = (double)CLAMP(sum);
}
}
ANS(i,i) = 1.0;
}
}
} /* cov_na_1() */
static void
cov_complete2(int n, int ncx, int ncy, double *x, double *y,
double *xm, double *ym, int *ind,
double *ans, Rboolean *sd_0, Rboolean cor, Rboolean kendall)
{
COV_init(ncy);
if(!kendall) {
MEAN(x);/* -> xm[] */
MEAN(y);/* -> ym[] */
n1 = nobs - 1;
}
for (i = 0 ; i < ncx ; i++) {
xx = &x[i * n];
if(!kendall) {
xxm = xm[i];
for (j = 0 ; j < ncy ; j++) {
yy = &y[j * n];
yym = ym[j];
sum = 0.;
for (k = 0 ; k < n ; k++)
if (ind[k] != 0)
sum += (LDOUBLE)(xx[k] - xxm) * (yy[k] - yym);
ANS(i,j) = (double)(sum / n1);
}
}
else { /* Kendall's tau */
for (j = 0 ; j < ncy ; j++) {
yy = &y[j * n];
sum = 0.;
for (k = 0 ; k < n ; k++)
if (ind[k] != 0)
for (n1 = 0 ; n1 < n ; n1++)
if (ind[n1] != 0)
sum += sign(xx[k] - xx[n1])
* sign(yy[k] - yy[n1]);
ANS(i,j) = (double)sum;
}
}
}
if (cor) {
#define COV_SDEV(_X_) \
for (i = 0 ; i < nc##_X_ ; i++) { /* Var(X[i]) */ \
xx = &_X_[i * n]; \
sum = 0.; \
if(!kendall) { \
xxm = _X_##m [i]; \
for (k = 0 ; k < n ; k++) \
if (ind[k] != 0) \
sum += (LDOUBLE)(xx[k] - xxm) * (xx[k] - xxm); \
sum /= n1; \
} \
else { /* Kendall's tau */ \
for (k = 0 ; k < n ; k++) \
if (ind[k] != 0) \
for (n1 = 0 ; n1 < n ; n1++) \
if (ind[n1] != 0 && xx[k] != xx[n1]) \
sum ++; /* = sign(. - .)^2 */ \
} \
_X_##m [i] = (double)SQRTL(sum); \
}
COV_SDEV(x); /* -> xm[.] */
COV_SDEV(y); /* -> ym[.] */
for (i = 0 ; i < ncx ; i++)
for (j = 0 ; j < ncy ; j++)
if (xm[i] == 0. || ym[j] == 0.) {
*sd_0 = TRUE;
ANS(i,j) = NA_REAL;
}
else {
ANS(i,j) /= (xm[i] * ym[j]);
ANS(i,j) = CLAMP(ANS(i,j));
}
}/* cor */
}/* cov_complete2 */
#undef COV_SDEV
static void
cov_na_2(int n, int ncx, int ncy, double *x, double *y,
double *xm, double *ym, int *has_na_x, int *has_na_y,
double *ans, Rboolean *sd_0, Rboolean cor, Rboolean kendall)
{
COV_ini_na(ncy);
if(!kendall) {
MEAN_(x, has_na_x);/* -> xm[] */
MEAN_(y, has_na_y);/* -> ym[] */
n1 = n - 1;
}
for (i = 0 ; i < ncx ; i++) {
if(has_na_x[i]) {
for (j = 0 ; j < ncy; j++)
ANS(i,j) = NA_REAL;
}
else {
xx = &x[i * n];
if(!kendall) {
xxm = xm[i];
for (j = 0 ; j < ncy ; j++)
if(has_na_y[j]) {
ANS(i,j) = NA_REAL;
} else {
yy = &y[j * n];
yym = ym[j];
sum = 0.;
for (k = 0 ; k < n ; k++)
sum += (LDOUBLE)(xx[k] - xxm) * (yy[k] - yym);
ANS(i,j) = (double)(sum / n1);
}
}
else { /* Kendall's tau */
for (j = 0 ; j < ncy ; j++)
if(has_na_y[j]) {
ANS(i,j) = NA_REAL;
} else {
yy = &y[j * n];
sum = 0.;
for (k = 0 ; k < n ; k++)
for (n1 = 0 ; n1 < n ; n1++)
sum += sign(xx[k] - xx[n1]) * sign(yy[k] - yy[n1]);
ANS(i,j) = (double)sum;
}
}
}
}
if (cor) {
#define COV_SDEV(_X_) \
for (i = 0 ; i < nc##_X_ ; i++) \
if(!has_na_##_X_[i]) { /* Var(X[j]) */ \
xx = &_X_[i * n]; \
sum = 0.; \
if(!kendall) { \
xxm = _X_##m [i]; \
for (k = 0 ; k < n ; k++) \
sum += (LDOUBLE)(xx[k] - xxm) * (xx[k] - xxm); \
sum /= n1; \
} \
else { /* Kendall's tau */ \
for (k = 0 ; k < n ; k++) \
for (n1 = 0 ; n1 < n ; n1++) \
if (xx[k] != xx[n1]) \
sum ++; /* = sign(. - .)^2 */ \
} \
_X_##m [i] = (double) SQRTL(sum); \
}
COV_SDEV(x); /* -> xm[.] */
COV_SDEV(y); /* -> ym[.] */
for (i = 0 ; i < ncx ; i++)
if(!has_na_x[i]) {
for (j = 0 ; j < ncy ; j++)
if(!has_na_y[j]) {
if (xm[i] == 0. || ym[j] == 0.) {
*sd_0 = TRUE;
ANS(i,j) = NA_REAL;
}
else {
ANS(i,j) /= (xm[i] * ym[j]);
ANS(i,j) = CLAMP(ANS(i,j));
}
}
}
}/* cor */
}/* cov_na_2 */
#undef ANS
#undef COV_init
#undef MEAN
#undef MEAN_
#undef COV_SDEV
/* complete[12]() returns indicator vector ind[] of complete.cases(), or
* -------------- if(na_fail) signals error if any NA/NaN is encountered
*/
/* This might look slightly inefficient, but it is designed to
* optimise paging in virtual memory systems ...
* (or at least that's my story, and I'm sticking to it.)
*/
#define NA_LOOP \
for (i = 0 ; i < n ; i++) \
if (ISNAN(z[i])) { \
if (na_fail) error(_("missing observations in cov/cor"));\
else ind[i] = 0; \
}
#define COMPLETE_1 \
double *z; \
int i, j; \
for (i = 0 ; i < n ; i++) \
ind[i] = 1; \
for (j = 0 ; j < ncx ; j++) { \
z = &x[j * n]; \
NA_LOOP \
}
static void complete1(int n, int ncx, double *x, int *ind, Rboolean na_fail)
{
COMPLETE_1
}
static void
complete2(int n, int ncx, int ncy, double *x, double *y, int *ind, Rboolean na_fail)
{
COMPLETE_1
for(j = 0 ; j < ncy ; j++) {
z = &y[j * n];
NA_LOOP
}
}
#define HAS_NA_1(_X_,_HAS_NA_) \
for (j = 0 ; j < nc##_X_ ; j++) { \
z = &_X_[j * n]; \
_HAS_NA_[j] = 0; \
for (i = 0 ; i < n ; i++) \
if (ISNAN(z[i])) { \
_HAS_NA_[j] = 1; break; \
} \
}
static void find_na_1(int n, int ncx, double *x, int *has_na)
{
double *z;
int i, j;
HAS_NA_1(x, has_na)
}
static void
find_na_2(int n, int ncx, int ncy, double *x, double *y, int *has_na_x, int *has_na_y)
{
double *z;
int i, j;
HAS_NA_1(x, has_na_x)
HAS_NA_1(y, has_na_y)
}
#undef NA_LOOP
#undef COMPLETE_1
#undef HAS_NA_1
/* co[vr](x, y, use =
{ 1, 2, 3, 4, 5 }
"all.obs", "complete.obs", "pairwise.complete", "everything", "na.or.complete"
kendall = TRUE/FALSE)
*/
static SEXP corcov(SEXP x, SEXP y, SEXP na_method, SEXP skendall, Rboolean cor)
{
SEXP ans, xm, ym, ind;
Rboolean ansmat, kendall, pair, na_fail, everything, sd_0, empty_err;
int i, method, n, ncx, ncy, nprotect = 2;
#define DEFUNCT_VAR_FACTOR
#ifdef DEFUNCT_VAR_FACTOR
# define VAR_FACTOR_MSG "Calling var(x) on a factor x is defunct.\n Use something like 'all(duplicated(x)[-1L])' to test for a constant vector."
#else
# define VAR_FACTOR_MSG "Calling var(x) on a factor x is deprecated and will become an error.\n Use something like 'all(duplicated(x)[-1L])' to test for a constant vector."
#endif
/* Arg.1: x */
if(isNull(x)) /* never allowed */
error(_("'x' is NULL"));
if(isFactor(x))
#ifdef DEFUNCT_VAR_FACTOR
error(_(VAR_FACTOR_MSG));
#else
warning(_(VAR_FACTOR_MSG));
#endif
/* length check of x -- only if(empty_err) --> below */
x = PROTECT(coerceVector(x, REALSXP));
if ((ansmat = isMatrix(x))) {
n = nrows(x);
ncx = ncols(x);
}
else {
n = length(x);
ncx = 1;
}
/* Arg.2: y */
if (isNull(y)) {/* y = x : var() */
ncy = ncx;
} else {
if(isFactor(y))
#ifdef DEFUNCT_VAR_FACTOR
error(_(VAR_FACTOR_MSG));
#else
warning(_(VAR_FACTOR_MSG));
#endif
y = PROTECT(coerceVector(y, REALSXP));
nprotect++;
if (isMatrix(y)) {
if (nrows(y) != n)
error(_("incompatible dimensions"));
ncy = ncols(y);
ansmat = TRUE;
}
else {
if (length(y) != n)
error(_("incompatible dimensions"));
ncy = 1;
}
}
/* Arg.3: method */
method = asInteger(na_method);
/* Arg.4: kendall */
kendall = asLogical(skendall);
/* "default: complete" (easier for -Wall) */
na_fail = FALSE; everything = FALSE; empty_err = TRUE;
pair = FALSE;
switch(method) {
case 1: /* use all : no NAs */
na_fail = TRUE;
break;
case 2: /* complete */
/* did na.omit in R */
if (!LENGTH(x)) error(_("no complete element pairs"));
break;
case 3: /* pairwise.complete */
pair = TRUE;
break;
case 4: /* "everything": NAs are propagated */
everything = TRUE;
empty_err = FALSE;
break;
case 5: /* "na.or.complete": NAs are propagated */
empty_err = FALSE;
break;
default:
error(_("invalid 'use' (computational method)"));
}
if (empty_err && !LENGTH(x))
error(_("'x' is empty"));
if (ansmat) PROTECT(ans = allocMatrix(REALSXP, ncx, ncy));
else PROTECT(ans = allocVector(REALSXP, ncx * ncy));
sd_0 = FALSE;
if (isNull(y)) {
if (everything) { /* NA's are propagated */
PROTECT(xm = allocVector(REALSXP, ncx));
PROTECT(ind = allocVector(LGLSXP, ncx));
find_na_1(n, ncx, REAL(x), /* --> has_na[] = */ LOGICAL(ind));
cov_na_1 (n, ncx, REAL(x), REAL(xm), LOGICAL(ind), REAL(ans), &sd_0, cor, kendall);
UNPROTECT(2);
}
else if (!pair) { /* all | complete "var" */
PROTECT(xm = allocVector(REALSXP, ncx));
PROTECT(ind = allocVector(INTSXP, n));
complete1(n, ncx, REAL(x), INTEGER(ind), na_fail);
cov_complete1(n, ncx, REAL(x), REAL(xm),
INTEGER(ind), REAL(ans), &sd_0, cor, kendall);
if(empty_err) {
Rboolean indany = FALSE;
for(i = 0; i < n; i++) {
if(INTEGER(ind)[i] == 1) { indany = TRUE; break; }
}
if(!indany) error(_("no complete element pairs"));
}
UNPROTECT(2);
}
else { /* pairwise "var" */
cov_pairwise1(n, ncx, REAL(x), REAL(ans), &sd_0, cor, kendall);
}
}
else { /* Co[vr] (x, y) */
if (everything) {
SEXP has_na_y;
PROTECT(xm = allocVector(REALSXP, ncx));
PROTECT(ym = allocVector(REALSXP, ncy));
PROTECT(ind = allocVector(LGLSXP, ncx));
PROTECT(has_na_y = allocVector(LGLSXP, ncy));
find_na_2(n, ncx, ncy, REAL(x), REAL(y), INTEGER(ind), INTEGER(has_na_y));
cov_na_2 (n, ncx, ncy, REAL(x), REAL(y), REAL(xm), REAL(ym),
INTEGER(ind), INTEGER(has_na_y), REAL(ans), &sd_0, cor, kendall);
UNPROTECT(4);
}
else if (!pair) { /* all | complete */
PROTECT(xm = allocVector(REALSXP, ncx));
PROTECT(ym = allocVector(REALSXP, ncy));
PROTECT(ind = allocVector(INTSXP, n));
complete2(n, ncx, ncy, REAL(x), REAL(y), INTEGER(ind), na_fail);
cov_complete2(n, ncx, ncy, REAL(x), REAL(y), REAL(xm), REAL(ym),
INTEGER(ind), REAL(ans), &sd_0, cor, kendall);
if(empty_err) {
Rboolean indany = FALSE;
for(i = 0; i < n; i++) {
if(INTEGER(ind)[i] == 1) { indany = TRUE; break; }
}
if(!indany) error(_("no complete element pairs"));
}
UNPROTECT(3);
}
else { /* pairwise */
cov_pairwise2(n, ncx, ncy, REAL(x), REAL(y), REAL(ans),
&sd_0, cor, kendall);
}
}
if (ansmat) { /* set dimnames() when applicable */
if (isNull(y)) {
x = getAttrib(x, R_DimNamesSymbol);
if (!isNull(x) && !isNull(VECTOR_ELT(x, 1))) {
PROTECT(ind = allocVector(VECSXP, 2));
SET_VECTOR_ELT(ind, 0, duplicate(VECTOR_ELT(x, 1)));
SET_VECTOR_ELT(ind, 1, duplicate(VECTOR_ELT(x, 1)));
setAttrib(ans, R_DimNamesSymbol, ind);
UNPROTECT(1);
}
}
else {
x = getAttrib(x, R_DimNamesSymbol);
y = getAttrib(y, R_DimNamesSymbol);
if ((length(x) >= 2 && !isNull(VECTOR_ELT(x, 1))) ||
(length(y) >= 2 && !isNull(VECTOR_ELT(y, 1)))) {
PROTECT(ind = allocVector(VECSXP, 2));
if (length(x) >= 2 && !isNull(VECTOR_ELT(x, 1)))
SET_VECTOR_ELT(ind, 0, duplicate(VECTOR_ELT(x, 1)));
if (length(y) >= 2 && !isNull(VECTOR_ELT(y, 1)))
SET_VECTOR_ELT(ind, 1, duplicate(VECTOR_ELT(y, 1)));
setAttrib(ans, R_DimNamesSymbol, ind);
UNPROTECT(1);
}
}
}
if(sd_0)/* only in cor() */
warning(_("the standard deviation is zero"));
UNPROTECT(nprotect);
return ans;
}