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 checkNames(SEXP, SEXP);static SEXP installAttrib(SEXP, SEXP, SEXP);static SEXP removeAttrib(SEXP, SEXP);SEXP comment(SEXP);SEXP commentgets(SEXP, SEXP);static SEXP stripAttrib(SEXP tag, SEXP lst){if(lst == R_NilValue) return lst;if(tag == TAG(lst)) return stripAttrib(tag, CDR(lst));CDR(lst) = stripAttrib(tag, CDR(lst));return lst;}SEXP getAttrib(SEXP vec, SEXP name){SEXP s, blank;int len, i, any;if (isString(name))name = install(CHAR(STRING(name)[0]));if ((name == R_NamesSymbol) && (isList(vec) || isLanguage(vec))) {len = length(vec);PROTECT(s = allocVector(STRSXP, len));blank = mkChar("");i = 0;any = 0;for (; vec != R_NilValue; vec=CDR(vec),i++) {if (TAG(vec) == R_NilValue)STRING(s)[i] = blank;else if (isSymbol(TAG(vec))) {any = 1;STRING(s)[i] = PRINTNAME(TAG(vec));}elseerror("getAttrib: invalid type for TAG\n");}UNPROTECT(1);if (any) return (s);return R_NilValue;}for (s = ATTRIB(vec); s != R_NilValue; s = CDR(s))if (TAG(s) == name) {NAMED(CAR(s)) = NAMED(vec);return CAR(s);}return R_NilValue;}SEXP setAttrib(SEXP vec, SEXP name, SEXP val){if (isString(name))name = install(CHAR(STRING(name)[0]));if (val == R_NilValue)return removeAttrib(vec, name);if (vec == R_NilValue)error("attempt to set an attribute on NULL\n");PROTECT(vec);PROTECT(name);val = duplicate(val);UNPROTECT(2);if (name == R_NamesSymbol)return namesgets(vec, val);else if (name == R_DimSymbol)return dimgets(vec, val);else if (name == R_DimNamesSymbol)return dimnamesgets(vec, val);else if (name == R_ClassSymbol)return classgets(vec, val);else if (name == R_LevelsSymbol)return levelsgets(vec, val);else if (name == R_TspSymbol)return tspgets(vec, val);else if (name == R_RowNamesSymbol)return rownamesgets(vec, val);else if (name == R_CommentSymbol)return commentgets(vec, val);elsereturn installAttrib(vec, name, val);}SEXP installAttrib(SEXP vec, SEXP name, SEXP val){SEXP s, t;PROTECT(vec);PROTECT(name);PROTECT(val);for (s = ATTRIB(vec); s != R_NilValue; s = CDR(s)) {if (TAG(s) == name) {CAR(s) = val;UNPROTECT(3);return val;}}s = allocList(1);CAR(s) = val;TAG(s) = name;if (ATTRIB(vec) == R_NilValue)ATTRIB(vec) = s;else {t = nthcdr(ATTRIB(vec), length(ATTRIB(vec)) - 1);SETCDR(t, s);}UNPROTECT(3);return val;}SEXP removeAttrib(SEXP vec, SEXP name){SEXP t;if (name == R_NamesSymbol && isList(vec)) {for (t = vec; t != R_NilValue; t = CDR(t))TAG(t) = R_NilValue;return R_NilValue;}else {if (name == R_DimSymbol)ATTRIB(vec) = stripAttrib(R_DimNamesSymbol, ATTRIB(vec));ATTRIB(vec) = stripAttrib(name, ATTRIB(vec));if (name == R_ClassSymbol)OBJECT(vec) = 0;}return R_NilValue;}void checkNames(SEXP x, SEXP s){if (isVector(x) || isList(x) || isLanguage(x)) {if (!isVector(s) && !isList(s))error("invalid type for names: must be vector\n");if (length(x) != length(s))error("names attribute must be the same length as the vector\n");}else error("names applied to non-vector\n");}/* Time Series Parameters */static void badtsp(){error("invalid time series parameters specified\n");}SEXP tspgets(SEXP vec, SEXP val){double start, end, frequency;int n;if (!isNumeric(val) || length(val) != 3)error("tsp attribute must be numeric of length three\n");if (isReal(val)) {start = REAL(val)[0];end = REAL(val)[1];frequency = REAL(val)[2];}else {start = (INTEGER(val)[0] == NA_INTEGER) ? NA_REAL : INTEGER(val)[0];end = (INTEGER(val)[1] == NA_INTEGER) ? NA_REAL : INTEGER(val)[1];frequency = (INTEGER(val)[2] == NA_INTEGER) ? NA_REAL : INTEGER(val)[2];}if(frequency <= 0) badtsp();n = nrows(vec);if(fabs(end - start - (n - 1)/frequency) > 1.e-5)badtsp();PROTECT(vec);val = allocVector(REALSXP, 3);PROTECT(val);REAL(val)[0] = start;REAL(val)[1] = end;REAL(val)[2] = frequency;installAttrib(vec, R_TspSymbol, val);UNPROTECT(2);return vec;}SEXP levelsgets(SEXP vec, SEXP levels){if(isFactor(vec) && LENGTH(levels) != LEVELS(vec))error("length of \"levels\" vector and number of levels differ\n");PROTECT(vec);PROTECT(levels = coerceVector(levels, STRSXP));installAttrib(vec, R_LevelsSymbol, levels);UNPROTECT(2);return vec;}SEXP commentgets(SEXP vec, SEXP comment){if(isNull(comment) || isString(comment)) {if(length(comment) <= 0) {ATTRIB(vec) = stripAttrib(R_CommentSymbol, vec);}else {installAttrib(vec, R_CommentSymbol, comment);}return R_NilValue;}error("attempt to set invalid comment attribute\n");}SEXP do_commentgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) == 2) CAR(args) = duplicate(CAR(args));if(length(CADR(args)) == 0) CADR(args) = R_NilValue;setAttrib(CAR(args), R_CommentSymbol, CADR(args));return CAR(args);}SEXP do_comment(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);return getAttrib(CAR(args), R_CommentSymbol);}SEXP classgets(SEXP vec, SEXP class){if(isNull(class) || isString(class)) {if(length(class) <= 0) {ATTRIB(vec) = stripAttrib(R_ClassSymbol, vec);OBJECT(vec) = 0;}else {if(streql(CHAR(STRING(class)[0]), "data.frame") &&!isList(vec) )error("attempt to make non-list a data frame\n");installAttrib(vec, R_ClassSymbol, class);OBJECT(vec) = 1;}return R_NilValue;}error("attempt to set invalid class attribute\n");}SEXP do_classgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) == 2) CAR(args) = duplicate(CAR(args));if(length(CADR(args)) == 0) CADR(args) = R_NilValue;setAttrib(CAR(args), R_ClassSymbol, CADR(args));return CAR(args);}SEXP do_class(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);return getAttrib(CAR(args), R_ClassSymbol);}SEXP do_levelsgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) == 2) CAR(args) = duplicate(CAR(args));setAttrib(CAR(args), R_LevelsSymbol, CADR(args));return CAR(args);}SEXP do_levels(SEXP call, SEXP op, SEXP args, SEXP env){SEXP ans;int i, n;char *s;checkArity(op, args);ans = getAttrib(CAR(args), R_LevelsSymbol);if(isFactor(CAR(args)) && ans == R_NilValue) {n = LEVELS(CAR(args));PROTECT(ans = allocVector(STRSXP, n));for(i=0 ; i<n ; i++) {s = Rsprintf("%d",i+1);STRING(ans)[i] = mkChar(s);}UNPROTECT(1);}return ans;}SEXP do_namesgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) == 2) CAR(args) = duplicate(CAR(args));setAttrib(CAR(args), R_NamesSymbol, CADR(args));return CAR(args);}SEXP namesgets(SEXP vec, SEXP val){int i;SEXP s,rval;PROTECT(vec);PROTECT(val);if( isList(val) )if( !isVectorizable(val) )error("incompatible names argument\n");else {rval = allocVector(STRSXP, length(vec));PROTECT(rval);for (i = 0; i < length(vec); i++) {s = coerceVector(CAR(val), STRSXP);STRING(rval)[i] = STRING(s)[0];}UNPROTECT(1);val = rval;}elseval = coerceVector(val, STRSXP);UNPROTECT(1);PROTECT(val);checkNames(vec, val);if (isList(vec) || isLanguage(vec)) {i=0;for (s = vec; s != R_NilValue; s = CDR(s), i++)if (STRING(val)[i] != R_NilValue&& STRING(val)[i] != R_NaString&& *CHAR(STRING(val)[i]) != 0)TAG(s) = install(CHAR(STRING(val)[i]));elseTAG(s) = R_NilValue;}else if (isVector(vec))installAttrib(vec, R_NamesSymbol, val);elseerror("invalid type to set names attribute\n");UNPROTECT(2);return vec;}SEXP do_names(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s, t;checkArity(op, args);s = CAR(args);if(isVector(s) || isList(s) || isLanguage(s)) {t = getAttrib(s, R_DimSymbol);if(TYPEOF(t) == INTSXP && length(t) == 1) {t = getAttrib(s, R_DimNamesSymbol);if(!isNull(t)) return CAR(t);}else return getAttrib(s, R_NamesSymbol);}return R_NilValue;}SEXP duplicated(SEXP);SEXP rownamesgets(SEXP vec, SEXP val){int i;SEXP dups;PROTECT(vec);PROTECT(val);dups=duplicated(val);for(i=0; i < length(dups) ; i++ )if( LOGICAL(dups)[i] ) {warning("some row names are duplicated; argument ignored\n");UNPROTECT(2);return vec;}if(isFrame(vec)) {val = coerceVector(val, STRSXP);UNPROTECT(1);PROTECT(val);if (nrows(CAR(vec)) != length(val))error("names attribute must be the same length as the vector\n");}installAttrib(vec, R_RowNamesSymbol, val);UNPROTECT(2);return vec;}SEXP do_rownames(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);return getAttrib(CAR(args), R_RowNamesSymbol);}SEXP do_dimnamesgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) > 2) CAR(args) = duplicate(CAR(args));if( isFrame(CAR(args)) ) {if( !isList(CADR(args)) )errorcall(call,"wrong argument type for new dimnames\n");switch( length(CADR(args)) ) {case 0:setAttrib(CAR(args), R_RowNamesSymbol, R_NilValue);setAttrib(CAR(args), R_NamesSymbol, R_NilValue);break;case 2:setAttrib(CAR(args), R_RowNamesSymbol, CAR(CADR(args)));setAttrib(CAR(args), R_NamesSymbol, CADR(CADR(args)));break;default:errorcall(call,"wrong length for new dimnames\n");}}elsesetAttrib(CAR(args), R_DimNamesSymbol, CADR(args));return CAR(args);}SEXP dimnamesgets(SEXP vec, SEXP val){SEXP dims, top;int k, i;PROTECT(vec);PROTECT(val);if (!isArray(vec) && !isList(vec) && !isFrame(vec))error("dimnames applied to non-array\n");if (!isList(val)) error("invalid type for dimnames: must be a list\n");dims = getAttrib(vec, R_DimSymbol);if (isFrame(vec)) {if(length(val) != 2)error("dimnames: number of dimensions must equal number of names\n");vec = rownamesgets(vec, CAR(val));UNPROTECT(2);PROTECT(vec);PROTECT(val);vec = namesgets(vec, CADR(val));UNPROTECT(2);return vec;}if ((k = LENGTH(dims)) != length(val))error("dimnames: number of dimensions must equal number of names\n");top = val;for (i = 0; i < k; i++) {if (CAR(val) != R_NilValue) {if (!isVector(CAR(val)))error("invalid type for dim name must be a vector\n");if (INTEGER(dims)[i] != LENGTH(CAR(val)) && LENGTH(CAR(val)) != 0)error("length of namelist must equal dims\n");if(LENGTH(CAR(val)) == 0) {CAR(val) = R_NilValue;}else if (!isString(CAR(val))) {CAR(val) = coerceVector(CAR(val), STRSXP);}}val = CDR(val);}installAttrib(vec, R_DimNamesSymbol, top);UNPROTECT(2);return (vec);}SEXP do_dimnames(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(isFrame(CAR(args))) {PROTECT(op = allocList(2));CAR(op) = getAttrib(CAR(args),R_RowNamesSymbol);CADR(op) = getAttrib(CAR(args),R_NamesSymbol);UNPROTECT(1);return op;}return (getAttrib(CAR(args), R_DimNamesSymbol));}SEXP do_dim(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(isFrame(CAR(args))) {op = allocVector(INTSXP, 2);INTEGER(op)[0] = nrows(CAAR(args));INTEGER(op)[1] = length(CAR(args));return op;}return (getAttrib(CAR(args), R_DimSymbol));}SEXP do_dimgets(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);if(NAMED(CAR(args)) > 1) CAR(args) = duplicate(CAR(args));setAttrib(CAR(args), R_DimSymbol, CADR(args));setAttrib(CAR(args), R_NamesSymbol, R_NilValue);return CAR(args);}SEXP dimgets(SEXP vec, SEXP val){int len, ndim, i, total;PROTECT(vec);PROTECT(val);if (!isVector(vec) && !isList(vec))error("dim<- : invalid first argument\n");if (!isVector(val) && !isList(val))error("dim<- : invalid second argument\n");val = coerceVector(val, INTSXP);UNPROTECT(1);PROTECT(val);len = length(vec);ndim = length(val);if( ndim == 0 )error("dim: Invalid dimension vector\n");total = 1;for (i=0; i<ndim; i++)total *= INTEGER(val)[i];if (total != len)error("dim<- length of dims do not match the length of object\n");removeAttrib(vec, R_DimNamesSymbol);installAttrib(vec, R_DimSymbol, val);UNPROTECT(2);return vec;}SEXP do_attributes(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s;s = R_NilValue;if (isList(CAR(args)) || isFrame(CAR(args)))s = getAttrib(CAR(args), R_NamesSymbol);PROTECT(s);if (s != R_NilValue) {s = CONS(s, ATTRIB(CAR(args)));TAG(s) = R_NamesSymbol;}elses = ATTRIB(CAR(args));UNPROTECT(1);NAMED(s) = NAMED(CAR(args));return s;}static SEXP dimptr;static SEXP TrimDim(SEXP l){if(l != R_NilValue) {if(TAG(l) == R_DimSymbol) {dimptr = l;return CDR(l);}else {CDR(l) = TrimDim(CDR(l));return l;}}return R_NilValue;}/* NOTE: The following code ensures that when an attribute list *//* is attached to an object, that the "dim" attibute is always *//* brought to the front of the list. This ensures that when both *//* "dim" and "dimnames" are set that the "dim" is attached first. */SEXP do_attributesgets(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s, t;if(CAR(args) == R_NilValue ) {warning("attempt to set attributes on NULL\n");return(R_NilValue);}if(NAMED(CAR(args)) == 2) CAR(args) = duplicate(CAR(args));s = CAR(args);t = CADR(args);if(isList(s) || isFrame(s))setAttrib(s, R_NamesSymbol, R_NilValue);ATTRIB(s) = R_NilValue;OBJECT(s) = 0;if (!isList(t))errorcall(call, "attributes must be in a list\n");/* Ghastly hack to ensure that "dim" *//* is always the first attribute */dimptr = R_NilValue;t = TrimDim(t);if(dimptr != R_NilValue) {CDR(dimptr) = t;t = dimptr;}for (; t != R_NilValue; t = CDR(t)) {if (TAG(t) == R_NilValue)error("all attributes must have names\n");setAttrib(s, TAG(t), CAR(t));}return s;}SEXP do_attr(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s, t;s = CAR(args);t = CADR(args);if (!isString(t))error("attribute name must be of mode character\n");return getAttrib(s, install(CHAR(STRING(t)[0])));}SEXP do_attrgets(SEXP call, SEXP op, SEXP args, SEXP env){SEXP obj, name, value;obj = eval(CAR(args), env);if(NAMED(obj) == 2)PROTECT(duplicate(obj));elsePROTECT(obj);PROTECT(name = eval(CADR(args), env));if (!isString(name))error("attr<- : name must be of mode character\n");/* rhs is already evaluated */PROTECT(value = CAR(CDDR(args)));setAttrib(obj, name, value);UNPROTECT(3);return obj;}