The R Project SVN R

Rev

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);
    else
        copyListMatrix(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;
    }
    else
        error("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 OLD
    switch(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;
    }
#else
    INTEGER(ans)[0] = length(CAR(args));
#endif
    return 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));
    else
        INTEGER(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_DATA
static 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:
            ;
        }
}
#endif

SEXP 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_DATA
    if (!(isNumeric(CAR(args)) || isComplex(CAR(args))) ||
        !(isNumeric(CADR(args)) || isComplex(CADR(args))))
#else
    if (!isNumeric(CAR(args)) || !isNumeric(CADR(args)))
#endif
        error("%%*%% 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_DATA
    if(isComplex(CAR(args)) || isComplex(CADR(args)))
        mode = CPLXSXP;
    else
#endif
        mode = 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_DATA
        if(mode == CPLXSXP)
            cmatprod(COMPLEX(CAR(args)), nrx, ncx, COMPLEX(CADR(args)), nry, ncy, COMPLEX(ans));
        else
#endif
            matprod(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_DATA
        if(mode == CPLXSXP)
            ccrossprod(COMPLEX(CAR(args)), nrx, ncx, COMPLEX(CADR(args)), nry, ncy, COMPLEX(ans));
        else
#endif
            crossprod(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_DATA
    case CPLXSXP:
        for (i = 0; i < len; i++)
            COMPLEX(r)[i] = COMPLEX(a)[(i / ncol) + (i % ncol) * nrow];
        break;
#endif
    case 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_DATA
    case CPLXSXP:
        for (i = 0; i < len; i++) {
            j = swap(i, dimsa, dimsr, perm, ind1, ind2);
            COMPLEX(r)[j] = COMPLEX(a)[i];
        }
        break;
#endif
    case 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);
    else
        setAttrib(r, R_DimSymbol, dimsa);
    UNPROTECT(6);
    return r;
}