Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** 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, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "Defn.h"#include "Mathlib.h"static void CheckDims(SEXP dims){int i;for (i = 0; i < LENGTH(dims); i++) {if (INTEGER(dims)[i] <= 0)error("invalid array extent\n");}}SEXP do_matrix(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP vals, snr, snc;int nr, nc, byrow, lendat;checkArity(op, args);vals = CAR(args);snr = CADR(args);snc = CADDR(args);byrow = asInteger(CADR(CDDR(args)));if (isVector(vals) || isList(vals)) {if(length(vals) <= 0)errorcall(call, "argument has length zero\n");} else errorcall(call, "invalid matrix element type\n");if (!isNumeric(snr) || !isNumeric(snc))error("non-numeric matrix extent\n");lendat = length(vals);nr = asInteger(snr);nc = asInteger(snc);if( lendat != 1 && (nr*nc) % lendat != 0 ) {if( ((lendat>nr) && (lendat/nr)*nr != lendat ) ||((lendat< nr) && (nr/lendat) * lendat != nr ))warning("Replacement length not a multiple of the elements to replace in matrix(...) \n");else if( ((lendat>nc) && (lendat/nc)*nc != lendat ) ||((lendat< nc) && (nc/lendat) * lendat != nc ))warning("Replacement length not a multiple of the elements to replace in matrix(...) \n");}PROTECT(snr = allocMatrix(TYPEOF(vals), nr, nc));LEVELS(snr) = LEVELS(vals);if(isVector(vals))copyMatrix(snr, vals, byrow);elsecopyListMatrix(snr, vals, byrow);UNPROTECT(1);return snr;}SEXP allocMatrix(SEXPTYPE mode, int nrow, int ncol){SEXP s, t;int n;if (nrow <= 0 || ncol <= 0)error("nonpositive extents to matrix\n");n = nrow * ncol;PROTECT(s = allocVector(mode, n));PROTECT(t = allocVector(INTSXP, 2));INTEGER(t)[0] = nrow;INTEGER(t)[1] = ncol;setAttrib(s, R_DimSymbol, t);UNPROTECT(2);return s;}SEXP do_array(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP vals, dims, ans;if (length(args) != 2)error("incorrect arg count to \"array\"\n");vals = CAR(args);dims = CADR(args);if (isVector(vals) && isNumeric(dims) && LENGTH(dims) >= 2) {PROTECT(dims = coerceVector(dims, INTSXP));CheckDims(dims);PROTECT(ans = allocArray(TYPEOF(vals), dims));LEVELS(ans) = LEVELS(vals);copyVector(ans, vals);UNPROTECT(2);return ans;}elseerror("bad arguments to array\n");/*NOTREACHED*/}SEXP allocArray(SEXPTYPE mode, SEXP dims){SEXP array;int i, n;n = 1;for (i = 0; i < LENGTH(dims); i++)n = n * INTEGER(dims)[i];PROTECT(dims = duplicate(dims));PROTECT(array = allocVector(mode, n));setAttrib(array, R_DimSymbol, dims);UNPROTECT(2);return array;}/* DropDims strips away redundant dimensioning *//* information If there is an appropriate dimnames *//* attribute the correct element is extracted and *//* attached to the vector as a names attribute. Note *//* that this function mutates x. Duplication should *//* occur before this is called. */SEXP DropDims(SEXP x){SEXP p, q, r, pdims, pdimnames;int i, n;/* Locate the dims and dimnames attributes */pdims = R_NilValue;pdimnames = R_NilValue;for(p=ATTRIB(x) ; p!=R_NilValue ; p=CDR(p)) {if(TAG(p) == R_DimSymbol)pdims = p;if(TAG(p) == R_DimNamesSymbol)pdimnames = p;}/* Check that dropping will actually do something */if(pdims == R_NilValue) return x;p = CAR(pdims);n = 0;for(i=0 ; i<LENGTH(p) ; i++)if(INTEGER(p)[i] != 1) n++;if(n == LENGTH(p)) return x;PROTECT(x);if(n <= 1) { /* vector */SEXP newnames = R_NilValue;if(pdimnames != R_NilValue) {q = CAR(pdimnames);for(i=0 ; i<LENGTH(p) ; i++) {if(INTEGER(p)[i] != 1)newnames = CAR(q);q = CDR(q);}}PROTECT(newnames);setAttrib(x, R_DimNamesSymbol, R_NilValue);setAttrib(x, R_DimSymbol, R_NilValue);setAttrib(x, R_NamesSymbol, newnames);UNPROTECT(1);}else { /* array */SEXP newdims, newdimnames;PROTECT(newdims = allocVector(INTSXP, n));p = CAR(pdims);n = 0;for(i=0 ; i<LENGTH(p) ; i++)if(INTEGER(p)[i] != 1)INTEGER(newdims)[n++] = INTEGER(p)[i];if(pdimnames) {PROTECT(newdimnames = allocList(n));q = CAR(pdimnames);r = newdimnames;for(i=0 ; i<LENGTH(p) ; i++) {if(INTEGER(p)[i] != 1) {CAR(r) = CAR(q);r = CDR(r);}q = CDR(q);}UNPROTECT(1);}CAR(pdims) = newdims;if(pdimnames != R_NilValue)CAR(pdimnames) = newdimnames;UNPROTECT(1);}UNPROTECT(1);return x;}SEXP do_drop(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP x, xdims;int i, n, shorten;checkArity(op, args);x = CAR(args);if((xdims = getAttrib(x, R_DimSymbol)) != R_NilValue) {n = LENGTH(xdims);shorten = 0;for(i=0 ; i<n ; i++)if(INTEGER(xdims)[i] <= 1) shorten = 1;if(shorten) {if(NAMED(x)) x = duplicate(x);x = DropDims(x);}}return x;}/* Length of Primitive Objects */SEXP do_length(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP ans;if (length(args) != 1)error("incorrect number of args to length\n");ans = allocVector(INTSXP, 1);#ifdef OLDswitch(TYPEOF(CAR(args))) {case NILSXP:INTEGER(ans)[0] = 0;break;case LGLSXP:case FACTSXP:case ORDSXP:case INTSXP:case REALSXP:case CPLXSXP:case STRSXP:case EXPRSXP:INTEGER(ans)[0] = LENGTH(CAR(args));break;case LISTSXP:case LANGSXP:INTEGER(ans)[0] = length(CAR(args));break;case ENVSXP:INTEGER(ans)[0] = length(FRAME(CAR(args)));break;default:INTEGER(ans)[0] = 1;break;}#elseINTEGER(ans)[0] = length(CAR(args));#endifreturn ans;}SEXP do_nlevels(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP ans;checkArity(op, args);ans = allocVector(INTSXP, 1);if (isFactor(CAR(args)))INTEGER(ans)[0] = LEVELS(CAR(args));elseINTEGER(ans)[0] = NA_INTEGER;return ans;}SEXP do_rowscols(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP ans;int i, j, nr, nc;if (length(args) != 1)error("incorrect number of args to row/col\n");if (!isMatrix(CAR(args)))error("a matrix is required as arg to row/col\n");nr = nrows(CAR(args));nc = ncols(CAR(args));ans = allocMatrix(INTSXP, nr, nc);switch (PRIMVAL(op)) {case 1:for (i = 0; i < nr; i++)for (j = 0; j < nc; j++)INTEGER(ans)[i + j * nr] = i + 1;break;case 2:for (i = 0; i < nr; i++)for (j = 0; j < nc; j++)INTEGER(ans)[i + j * nr] = j + 1;break;}return ans;}static void matprod(double *x, int nrx, int ncx, double *y, int nry, int ncy, double *z){int i, j, k;double xij, yjk, sum;for (i = 0; i < nrx; i++)for (k = 0; k < ncy; k++) {z[i + k * nrx] = NA_REAL;sum = 0.0;for (j = 0; j < ncx; j++) {xij = x[i + j * nrx];yjk = y[j + k * nry];if (!FINITE(xij) || !FINITE(yjk))goto next_ik;sum += xij * yjk;}z[i + k * nrx] = sum;next_ik:;}}static void cmatprod(complex *x, int nrx, int ncx,complex *y, int nry, int ncy, complex *z){int i, j, k;double xij_r, xij_i, yjk_r, yjk_i, sum_i, sum_r;for (i=0; i<nrx; i++)for (k=0; k<ncy; k++) {z[i+k*nrx].r = NA_REAL;z[i+k*nrx].i = NA_REAL;sum_r = 0.0;sum_i = 0.0;for (j=0; j<ncx; j++) {xij_r = x[i+j*nrx].r;xij_i = x[i+j*nrx].i;yjk_r = y[j+k*nry].r;yjk_i = y[j+k*nry].i;if (!FINITE(xij_r) || !FINITE(xij_i)|| !FINITE(yjk_r) || !FINITE(yjk_i))goto next_ik;sum_r += (xij_r * yjk_r - xij_i * yjk_i);sum_i += (xij_r * yjk_i + xij_i * yjk_r);}z[i+k*nrx].r = sum_r;z[i+k*nrx].i = sum_i;next_ik:;}}static void crossprod(double *x, int nrx, int ncx, double *y, int nry, int ncy, double *z){int i, j, k;double xji, yjk, sum;for (i = 0; i < ncx; i++)for (k = 0; k < ncy; k++) {z[i + k * ncx] = NA_REAL;sum = 0.0;for (j = 0; j < nrx; j++) {xji = x[j + i * nrx];yjk = y[j + k * nry];if (!FINITE(xji) || !FINITE(yjk))goto next_ik;sum += xji * yjk;}z[i + k * ncx] = sum;next_ik:;}}#ifdef COMPLEX_DATAstatic void ccrossprod(complex *x, int nrx, int ncx, complex *y, int nry, int ncy, complex *z){int i, j, k;double xji_r, xji_i, yjk_r, yjk_i, sum_r, sum_i;for (i = 0; i < ncx; i++)for (k = 0; k < ncy; k++) {z[i + k * ncx].r = NA_REAL;z[i + k * ncx].i = NA_REAL;sum_r = 0.0;sum_i = 0.0;for (j = 0; j < nrx; j++) {xji_r = x[j + i * nrx].r;xji_i = x[j + i * nrx].i;yjk_r = y[j + k * nry].r;yjk_i = y[j + k * nry].i;if (!FINITE(xji_r) || !FINITE(xji_i)|| !FINITE(yjk_r) || !FINITE(yjk_i))goto next_ik;sum_r += (xji_r * yjk_r - xji_i * yjk_i);sum_i += (xji_r * yjk_i + xji_i * yjk_r);}z[i + k * ncx].r = sum_r;z[i + k * ncx].i = sum_i;next_ik:;}}#endifSEXP do_matprod(SEXP call, SEXP op, SEXP args, SEXP rho){int ldx, ldy, nrx, ncx, nry, ncy, mode;SEXP x, y, xdims, ydims, ans;#ifdef COMPLEX_DATAif (!(isNumeric(CAR(args)) || isComplex(CAR(args))) ||!(isNumeric(CADR(args)) || isComplex(CADR(args))))#elseif (!isNumeric(CAR(args)) || !isNumeric(CADR(args)))#endiferror("%%*%% requires numeric matrix/vector arguments\n");x = CAR(args);y = CADR(args);xdims = getAttrib(x, R_DimSymbol);ydims = getAttrib(y, R_DimSymbol);ldx = LENGTH(xdims);ldy = LENGTH(ydims);if (ldx != 2 && ldy != 2) {if(PRIMVAL(op) == 0) {nrx = 1;ncx = LENGTH(x);}else {nrx = LENGTH(x);ncx = 1;}nry = LENGTH(y);ncy = 1;}else if (ldx != 2) {nry = INTEGER(ydims)[0];ncy = INTEGER(ydims)[1];nrx = 0;ncx = 0;if(PRIMVAL(op) == 0) {if(LENGTH(x) == nry) {nrx = 1;ncx = LENGTH(x);}}else {if(LENGTH(x) == nry) {nrx = LENGTH(x);ncx = 1;}}}else if (ldy != 2) {nrx = INTEGER(xdims)[0];ncx = INTEGER(xdims)[1];nry = 0;ncy = 0;if(PRIMVAL(op) == 0) {if (LENGTH(y) == ncx) {nry = LENGTH(y);ncy = 1;}}else {if (LENGTH(y) == nrx) {nry = LENGTH(y);ncy = 1;}}}else {nrx = INTEGER(xdims)[0];ncx = INTEGER(xdims)[1];nry = INTEGER(ydims)[0];ncy = INTEGER(ydims)[1];}if(PRIMVAL(op) == 0) {if(ncx != nry)errorcall(call, "non-conformable arguments\n");}else {if(nrx != nry)errorcall(call, "non-conformable arguments\n");}#ifdef COMPLEX_DATAif(isComplex(CAR(args)) || isComplex(CADR(args)))mode = CPLXSXP;else#endifmode = REALSXP;CAR(args) = coerceVector(CAR(args), mode);CADR(args) = coerceVector(CADR(args), mode);if(PRIMVAL(op) == 0) {PROTECT(ans = allocMatrix(mode, nrx, ncy));#ifdef COMPLEX_DATAif(mode == CPLXSXP)cmatprod(COMPLEX(CAR(args)), nrx, ncx, COMPLEX(CADR(args)), nry, ncy, COMPLEX(ans));else#endifmatprod(REAL(CAR(args)), nrx, ncx, REAL(CADR(args)), nry, ncy, REAL(ans));PROTECT(xdims = getAttrib(CAR(args), R_DimNamesSymbol));PROTECT(ydims = getAttrib(CADR(args), R_DimNamesSymbol));if (xdims != R_NilValue || ydims != R_NilValue) {setAttrib(ans, R_DimNamesSymbol, list2(CAR(xdims), CADR(ydims)));}}else {PROTECT(ans = allocMatrix(mode, ncx, ncy));#ifdef COMPLEX_DATAif(mode == CPLXSXP)ccrossprod(COMPLEX(CAR(args)), nrx, ncx, COMPLEX(CADR(args)), nry, ncy, COMPLEX(ans));else#endifcrossprod(REAL(CAR(args)), nrx, ncx, REAL(CADR(args)), nry, ncy, REAL(ans));PROTECT(xdims = getAttrib(CAR(args), R_DimNamesSymbol));PROTECT(ydims = getAttrib(CADR(args), R_DimNamesSymbol));if (xdims != R_NilValue || ydims != R_NilValue) {setAttrib(ans, R_DimNamesSymbol, list2(CADR(xdims), CADR(ydims)));}}UNPROTECT(3);return ans;}SEXP do_transpose(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP a, r, dims, dn;int i, len, ncol, nrow;checkArity(op, args);a = CAR(args);if(isVector(a)) {dims = getAttrib(a, R_DimSymbol);switch(length(dims)) {case 0:case 1:nrow = len = length(a);ncol = 1;break;case 2:ncol = ncols(a);nrow = nrows(a);len = length(a);break;default:goto not_matrix;}}else if(isList(a)) {dims = getAttrib(a, R_DimSymbol);if(length(dims) == 2) {errorcall(call, "can't transpose list matrices (yet)\n");}else goto not_matrix;}else goto not_matrix;PROTECT(r = allocVector(TYPEOF(a), len));switch (TYPEOF(a)) {case LGLSXP:case FACTSXP:case ORDSXP:case INTSXP:for (i = 0; i < len; i++)INTEGER(r)[i] = INTEGER(a)[(i / ncol) + (i % ncol) * nrow];break;case REALSXP:for (i = 0; i < len; i++)REAL(r)[i] = REAL(a)[(i / ncol) + (i % ncol) * nrow];break;#ifdef COMPLEX_DATAcase CPLXSXP:for (i = 0; i < len; i++)COMPLEX(r)[i] = COMPLEX(a)[(i / ncol) + (i % ncol) * nrow];break;#endifcase STRSXP:for (i = 0; i < len; i++)STRING(r)[i] = STRING(a)[(i / ncol) + (i % ncol) * nrow];break;}dims = allocVector(INTSXP, 2);INTEGER(dims)[0] = ncol;INTEGER(dims)[1] = nrow;setAttrib(r, R_DimSymbol, dims);if(!isNull(dn = getAttrib(a, R_DimNamesSymbol))) {PROTECT(dn = duplicate(dn));switch(length(dn)) {case 1:PROTECT(dims = allocList(2));CADR(dims) = CAR(dn);setAttrib(r, R_DimNamesSymbol, dims);UNPROTECT(1);break;case 2:dims = CAR(dn);CAR(dn) = CADR(dn);CADR(dn) = dims;setAttrib(r, R_DimNamesSymbol, dn);break;}UNPROTECT(1);}UNPROTECT(1);return r;not_matrix:errorcall(call, "argument is not a matrix\n");}/* swap works by finding for a index i, the position *//* in the array with dimensions dims1 in terms of *//* (i, j, k, l, m, ...); i.e. component-wise position, *//* then permute these to the order of the array with *//* dimensions dims2 and work backwards to an integer *//* offset in this array */static int swap(int ival, SEXP dims1, SEXP dims2, SEXP perm, SEXP ind1, SEXP ind2){int len, t1, i;len = length(dims1);t1 = ival;for (i = 0; i < len; i++) {INTEGER(ind1)[i] = t1 % INTEGER(dims1)[i];t1 = t1 / INTEGER(dims1)[i];}for (i = 0; i < len; i++)INTEGER(ind2)[i] = INTEGER(ind1)[(INTEGER(perm)[i] - 1)];t1 = INTEGER(ind2)[(len - 1)];for (i = (len - 2); i >= 0; i--) {t1 *= INTEGER(dims2)[i];t1 += INTEGER(ind2)[i];}return t1;}SEXP do_aperm(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP a, perm, r, dimsa, dimsr, ind1, ind2;int i, j, len;checkArity(op, args);a = CAR(args);PROTECT(dimsa = getAttrib(a, R_DimSymbol));if (dimsa == R_NilValue)error("aperm: invalid first argument, must be an array\n");PROTECT(perm = coerceVector(CADR(args), INTSXP));if (!isVector(perm) || (length(perm) != length(dimsa)))error("aperm: invalid second argument, must be a vector\n");len = length(a);PROTECT(dimsr = allocVector(INTSXP, length(dimsa)));for (i = 0; i < length(dimsa); i++)INTEGER(dimsr)[i] = INTEGER(dimsa)[(INTEGER(perm)[i] - 1)];PROTECT(r = allocVector(TYPEOF(a), len));PROTECT(ind1 = allocVector(INTSXP, LENGTH(dimsa)));PROTECT(ind2 = allocVector(INTSXP, LENGTH(dimsa)));switch (TYPEOF(a)) {case INTSXP:case FACTSXP:case ORDSXP:case LGLSXP:for (i = 0; i < len; i++) {j = swap(i, dimsa, dimsr, perm, ind1, ind2);INTEGER(r)[j] = INTEGER(a)[i];}break;case REALSXP:for (i = 0; i < len; i++) {j = swap(i, dimsa, dimsr, perm, ind1, ind2);REAL(r)[j] = REAL(a)[i];}break;#ifdef COMPLEX_DATAcase CPLXSXP:for (i = 0; i < len; i++) {j = swap(i, dimsa, dimsr, perm, ind1, ind2);COMPLEX(r)[j] = COMPLEX(a)[i];}break;#endifcase STRSXP:for (i = 0; i < len; i++) {j = swap(i, dimsa, dimsr, perm, ind1, ind2);STRING(r)[j] = STRING(a)[i];}break;default:errorcall(call, "invalid argument\n");}if (INTEGER(CAR(CDDR(args)))[0])setAttrib(r, R_DimSymbol, dimsr);elsesetAttrib(r, R_DimSymbol, dimsa);UNPROTECT(6);return r;}