#include #include #include #define TRUE 1 #define FALSE 0 typedef struct table_ Table; typedef struct dependency_ Dependency; /* any table */ struct table_ { int nRow; /* number of rows underneath header */ int nCol; /* number of columns */ char **name; /* column names */ char **unit; /* column units */ int *colIsNum; /* for each column, information whether it is numerical or not */ double **value; /* value[i] is column # i if floating point */ char ***text; /* text[i] is column # i if text */ }; struct dependency_ { int nArg; /* number of arguments */ int resultIndex; /* in the table: column index of the result */ int *argIndex; /* in the table: column indexes of the arguments */ }; Table *readTable(char *fn); Dependency *getDependency(char *s,Table *t); void writeTable(Table *t,char *fn); void freeTable(Table *t) void appendColumn(Table *t,char *name,char *unit); Table *readTable(char *fn) /*---------------------------------------------------------------------- * Reads a data table from a file. Criteria to distinguish between * header and body: * - row of column names in brackets * - change of number of columns * - no numerical columns * - row begins with # * All of these are considered header rows. Thus, the body may contain * also non-numerical columns, but must contain at least one numerical column. * * Input parameters: * fn file name * * Return value: * Pointer to a struct with number of header rows, numerical rows * and columns, column names and units and the values. * * Terms of use: * You are not allowed to claim that this code is the work of anybody else * than Henning Weede. * The author is not liable for any damage caused by this code. * The author has no duty to waist his time answering questions related * to this code. * The GNU Public License applies. * * Author: Henning Weede * Year: 2009 *---------------------------------------------------------------------- */{ int colIsNum,hasNum,i,iCol,iRow,isPossi,isUnitLine,nCol,nHead,nHeadPossi,nonBlank,possiCount,possiName,possiUnit,prevnCol,prevNonBlank; static char lline[2048]; static char *word[1024]; Table *t; FILE *fp; /* allocate result */ if((t=(Table *)calloc(1,sizeof(Table)))==(Table *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } /* open file */ if((fp=fopen(fn,"r"))==(FILE *)NULL) { fprintf(stderr,"Cannot read file %s.\n",fn); exit(1); } /* determine number of rows and columns */ for(iRow=nHead=0,prevnCol=nHeadPossi=-1,rewind(fp); ; ++iRow,prevnCol=nCol) { /* read next line */ fgets(lline,sizeof(lline)-3,fp); if(feof(fp)) break; if(strlen(lline)>sizeof(lline)-5) { fprintf(stderr,"Line(s) in file %s too long.\n",fn); exit(1); } /* split line into words */ for(i=nCol=0,prevNonBlank=FALSE; lline[i]; ++i,prevNonBlank=nonBlank) { nonBlank=((unsigned char)lline[i]>' ')&&(lline[i]!=';'); if((!prevNonBlank) && nonBlank) word[nCol++] = &lline[i]; if(prevNonBlank && (!nonBlank)) lline[i]=(char)0; } /* does this row have any numerical column? */ for(iCol=0,hasNum=FALSE; iColnRow=iRow-nHead; t->nCol=nCol; isPossi = (nHead==nHeadPossi); /* some checks */ if(t->nRow<4) { fprintf(stderr,"File %s has only %d numerical rows.\n",fn,t->nRow); exit(1); } if(t->nCol<2) { fprintf(stderr,"File %s has only %d columns.\n",fn,t->nCol); exit(1); } /* allocate */ if( ((t->name=(char **)calloc(t->nCol,sizeof(char *)))==(char **)NULL) || ((t->unit=(char **)calloc(t->nCol,sizeof(char *)))==(char **)NULL) || ((t->colIsNum=(int *)calloc(t->nCol,sizeof(int)))==(int *)NULL) || ((t->value=(double **)calloc(t->nCol,sizeof(double *)))==(double **)NULL) || ((t->text=(char ***)calloc(t->nCol,sizeof(char **)))==(char ***)NULL) ) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } for(iCol=0; iColnCol; ++iCol) { if( ((t->name[iCol]=(char *)calloc(32,sizeof(char)))==(char *)NULL) || ((t->unit[iCol]=(char *)calloc(32,sizeof(char)))==(char *)NULL) || ((t->text[iCol]=(char **)calloc(t->nRow,sizeof(char *)))==(char **)NULL) ) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } t->name[iCol][0]=(char)0; strcpy(t->unit[iCol],"-"); t->value[iCol]=(double *)NULL; } /* read column names and units */ for(iRow=0,possiName=possiUnit=FALSE,rewind(fp); iRow' ')&&(lline[i]!=';'); if((!prevNonBlank) && nonBlank) word[nCol++] = &lline[i]; if(prevNonBlank && (!nonBlank)) lline[i]=(char)0; } if(isPossi) { if(nCol==0) possiName=possiUnit=FALSE; if(possiName || possiUnit) { for(iCol=0; (iColnCol); ++iCol) { if(possiName) { if((t->name[possiCount]=(char *)realloc((void *)t->name[possiCount],(strlen(word[iCol])+32)*sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->name[possiCount++],word[iCol]); } else { if((t->unit[possiCount]=(char *)realloc((void *)t->unit[possiCount],(strlen(word[iCol])+32)*sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->unit[possiCount++],word[iCol]); } } } if((strncmp(lline,"Kanal.Name",10)==0)||(strncmp(lline,"Kanal.Soll.Name",15)==0)) { possiName=TRUE; possiUnit=FALSE; possiCount=0; } if((strncmp(lline,"Kanal.Einheit",13)==0)||(strncmp(lline,"Kanal.Soll.Einheit",18)==0)) { possiUnit=TRUE; possiName=FALSE; possiCount=0; } } else { /* does this row have any numerical column? */ for(iCol=0,hasNum=FALSE; iColnCol)&&(!hasNum)&&(lline[0]!='#')) for(iCol=0; iColnCol; ++iCol) { if(isUnitLine) { if((t->unit[iCol]=(char *)realloc((void *)t->unit[iCol],(strlen(word[iCol])+32)*sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->unit[iCol],&word[iCol][1]); t->unit[iCol][strlen(t->unit[iCol])-1]=(char)0; } else { if((t->name[iCol]=(char *)realloc((void *)t->name[iCol],(strlen(word[iCol])+32)*sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->name[iCol],word[iCol]); } } } } /* reset all columns to be numerical */ for(iCol=0; iColnCol; ++iCol) t->colIsNum[iCol]=TRUE; /* read data as text */ for(iRow=0,rewind(fp); iRownRow; ++iRow) { /* read next line */ fgets(lline,sizeof(lline)-3,fp); if(feof(fp)) break; /* split line into words */ for(i=nCol=0,prevNonBlank=FALSE; lline[i]; ++i,prevNonBlank=nonBlank) { nonBlank=((unsigned char)lline[i]>' ')&&(lline[i]!=';'); if((!prevNonBlank) && nonBlank) word[nCol++] = &lline[i]; if(prevNonBlank && (!nonBlank)) lline[i]=(char)0; } /* copy words to table and detect non-numerical columns */ for(iCol=0; iColtext[iCol][iRow]=(char *)calloc(strlen(word[iCol])+32,sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->text[iCol][iRow],word[iCol]); for(i=0; t->text[iCol][iRow][i]; ++i) if( (!isdigit(t->text[iCol][iRow][i]))&& (tolower(t->text[iCol][iRow][i])!='e')&& (t->text[iCol][iRow][i]!='.')&& (t->text[iCol][iRow][i]!='+')&& (t->text[iCol][iRow][i]!='-') ) t->colIsNum[iCol]=FALSE; } } /* replace text columns by numbers where necessary */ for(iCol=0; iColnCol; ++iCol) if(t->colIsNum[iCol]) { if((t->value[iCol]=(double *)calloc(t->nRow,sizeof(double)))==(double *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } for(iRow=0; iRownRow; ++iRow) { t->value[iCol][iRow]=atof(t->text[iCol][iRow]); free(t->text[iCol][iRow]); } free(t->text[iCol]); t->text[iCol]=(char **)NULL; } fclose(fp); return(t); } /* readTable() */ void writeTable(Table *t,char *fn) /*---------------------------------------------------------------------- * Writes a data table to a blank separated ASCII file * beginning with one row of column titles and one row of column units. * * Input parameters: * t the data table * fn file name * * Terms of use: * You are not allowed to claim that this code is the work of anybody else * than Henning Weede. * The author is not liable for any damage caused by this code. * The author has no duty to waist his time answering questions related * to this code. * The GNU Public License applies. * * Author: Henning Weede * Year: 2009 *---------------------------------------------------------------------- */{ int iCol,iRow,j; double max1,max2,mean,sdev; int *colWidth,*isInt; double *norm; char **fmt; static char s[64],s2[64]; FILE *fp; if( ((isInt=(int *)calloc(t->nCol,sizeof(int)))==(int *)NULL) || ((colWidth=(int *)calloc(t->nCol,sizeof(int)))==(int *)NULL) || ((norm=(double *)calloc(t->nCol,sizeof(double)))==(double *)NULL) || ((fmt=(char **)calloc(t->nCol,sizeof(char *)))==(char **)NULL) ) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } for(iCol=0; iColnCol; ++iCol) if((fmt[iCol]=(char *)calloc(32,sizeof(char)))==(char *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } /* which columns are integer? */ for(iCol=0; iColnCol; ++iCol) { isInt[iCol]=FALSE; if(t->colIsNum[iCol]) for(iRow=0,isInt[iCol]=TRUE; iRownRow; ++iRow) { if(fabs(t->value[iCol][iRow])>=16384.) isInt[iCol]=FALSE; if(fabs(t->value[iCol][iRow]-(int)t->value[iCol][iRow])>1.e-8) isInt[iCol]=FALSE; } } /* set column widths */ for(iCol=0; iColnCol; ++iCol) for(iCol=0; iColnCol; ++iCol) { colWidth[iCol] = (t->colIsNum[iCol] && (!isInt[iCol])) ? 10 : 0; if(colWidth[iCol]name[iCol])) colWidth[iCol]=strlen(t->name[iCol]); if(colWidth[iCol]unit[iCol])+2) colWidth[iCol]=strlen(t->unit[iCol])+2; if(!t->colIsNum[iCol]) for(iRow=0; iRownRow; ++iRow) if(colWidth[iCol]text[iCol][iRow])) colWidth[iCol]=strlen(t->text[iCol][iRow]); if(isInt[iCol]) for(iRow=0; iRownRow; ++iRow) { j=(int)log10(t->value[iCol][iRow])+2; if(colWidth[iCol]nCol; ++iCol) { if(t->colIsNum[iCol]) { if(isInt[iCol]) sprintf(fmt[iCol],"%%%dd ",colWidth[iCol]); else { for(iRow=0,mean=max1=0.; iRownRow; ++iRow) { if(max1value[iCol][iRow])) max1=fabs(t->value[iCol][iRow]); mean += t->value[iCol][iRow]; } mean = mean/t->nRow; for(iRow=0,sdev=0.; iRownRow; ++iRow) sdev += pow(t->value[iCol][iRow]-mean,2.); sdev = sqrt(sdev/t->nRow); max2 = (fabs(mean+3*sdev)>fabs(mean-3*sdev)) ? fabs(mean+3*sdev) : fabs(mean-3*sdev); norm[iCol] = (max1=1.e-3)&&(norm[iCol]<1.e7)) { j = 7-(int)log10(norm[iCol]); if(j>7) j=7; if(j<0) j=0; sprintf(fmt[iCol],"%%%d.%df ",colWidth[iCol],j); } else { if(norm[iCol]>1.e-8) { if(colWidth[iCol]<13) colWidth[iCol]=13; sprintf(fmt[iCol],"%%%d.5e ",colWidth[iCol]); } else sprintf(fmt[iCol],"%%%d.%df ",colWidth[iCol],colWidth[iCol]-3); } } } else sprintf(fmt[iCol],"%%%ds ",colWidth[iCol]); } /* open file */ if((fp=fopen(fn,"w"))==(FILE *)NULL) { fprintf(stderr,"Cannot write file %s.\n",fn); exit(1); } rewind(fp); /* write headers */ for(iCol=0; iColnCol; ++iCol) { sprintf(s,"%%%ds ",colWidth[iCol]); fprintf(fp,s,t->name[iCol]); } fprintf(fp,"\n"); for(iCol=0; iColnCol; ++iCol) { sprintf(s,"[%s]",t->unit[iCol]); sprintf(s2,"%%%ds ",colWidth[iCol]); fprintf(fp,s2,s); } fprintf(fp,"\n"); /* write table */ for(iRow=0; iRownRow; ++iRow) { for(iCol=0; iColnCol; ++iCol) { if(t->colIsNum[iCol]) { if(isInt[iCol]) fprintf(fp,fmt[iCol],(int)t->value[iCol][iRow]); else { if(fabs(t->value[iCol][iRow])<=norm[iCol]) fprintf(fp,fmt[iCol],t->value[iCol][iRow]); else { if((fabs(t->value[iCol][iRow])>=1.e-3)&&(fabs(t->value[iCol][iRow])<1.e7)) { j = 7-(int)log10(fabs(t->value[iCol][iRow])); if(j<0) j=0; if(j>7) j=7; sprintf(s,"%%%d.%df ",colWidth[iCol],j); } else sprintf(s,"%%%d.5e ",colWidth[iCol]); fprintf(fp,s,t->value[iCol][iRow]); } } } else fprintf(fp,fmt[iCol],t->text[iCol][iRow]); } fprintf(fp,"\n"); } fclose(fp); for(iCol=t->nCol-1; iCol>=0; --iCol) free(fmt[iCol]); free(fmt); free(norm); free(colWidth); free(isInt); } /* writeTable() */ Dependency *getDependency(char *expr,Table *t) /*---------------------------------------------------------------------- * Converts a string with the syntax y(x1,x2,x3,...) into a logical * description of how one column in a table depends on others. * * Input parameter: * t pointer to a struct with the column names (and other contents) * expr a string with the syntax y(x1,x2,x2,...) * * Return value: * pointer to a struct with the number of arguments, the * index of the resulting column and the indexes of the causing columns * * Terms of use: * You are not allowed to claim that this code is the work of anybody else * than Henning Weede. * The author is not liable for any damage caused by this code. * The author has no duty to waist his time answering questions related * to this code. * The GNU Public License applies. * * Author: Henning Weede * Year: 2009 *--------------------------------------------------------------------- */{ char c; int i,index,wordBeginning,wordEnd; static char s[1024]; Dependency *d; /* local copy */ strcpy(s,expr); /* allocate result */ if((d=(Dependency *)calloc(1,sizeof(Dependency)))==(Dependency *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } if((d->argIndex=(int *)calloc(1024,sizeof(int)))==(int *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } /* loop over the words */ for(i=wordBeginning=0,d->nArg=-1; s[i]; ++i) if((s[i]==',')||(s[i]=='(')||(s[i]==')')) { wordEnd = i; c=s[i]; s[i]=(char)0; /* which column name is this word? */ for(index=0; indexnCol; ++index) if(strcmp(&s[wordBeginning],t->name[index])==0) break; if(index >= t->nCol) { fprintf(stderr,"No such column %s in data file\n",&s[wordBeginning]); exit(1); } s[i]=c; /* assign index */ if(d->nArg<0) d->resultIndex=index; else d->argIndex[d->nArg]=index; /* increase number of arguments */ if(++d->nArg>1020) { fprintf(stderr,"Syntax error in %s.\n",s); exit(1); } /* next word begins after delimiter */ wordBeginning = wordEnd+1; } /* check result */ if(d->nArg < 1) { fprintf(stderr,"Syntax error: %s is not like y(x1,x2,x3,...)\n",s); exit(1); } /* reallocate */ if((d->argIndex=(int *)realloc((void *)d->argIndex,d->nArg*sizeof(int)))==(int *)NULL) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } return(d); } /* getDependency() */ void freeTable(Table *t) /*---------------------------------------------------------------------- * Frees the Table struct. * * Terms of use: * You are not allowed to claim that this code is the work of anybody else * than Henning Weede. * The author is not liable for any damage caused by this code. * The author has no duty to waist his time answering questions related * to this code. * The GNU Public License applies. * * Author: Henning Weede * Year: 2009 *---------------------------------------------------------------------- */{ int i,j; if(!t) return; if(t->text) { for(i=t->nCol-1; i>=0; --i) if(t->text[i]) { for(j=t->nRow-1; j>=0; --j) if(t->text[i][j]) free(t->text[i][j]); free(t->text[i]); } free(t->text); } if(t->value) { for(i=t->nCol-1; i>=0; --i) if(t->value[i]) free(t->value[i]); free(t->value); } if(t->unit) { for(i=t->nCol-1; i>=0; --i) if(t->unit[i]) free(t->unit[i]); free(t->unit); } if(t->name) { for(i=t->nCol-1; i>=0; --i) if(t->name[i]) free(t->name[i]); free(t->name); } if(t->colIsNum) free(t->colIsNum); } /* freeTable() */ void appendColumn(Table *t,char *name,char *unit) /*---------------------------------------------------------------------- * Appends a numerical column to the table. * * Terms of use: * You are not allowed to claim that this code is the work of anybody else * than Henning Weede. * The author is not liable for any damage caused by this code. * The author has no duty to waist his time answering questions related * to this code. * The GNU Public License applies. * * Author: Henning Weede * Year: 2009 *---------------------------------------------------------------------- */{ int n; n = t->nCol++; if( ((t->name = (char **)realloc((void *)t->name, t->nCol*sizeof(char *))) ==(char **)NULL) || ((t->unit = (char **)realloc((void *)t->unit, t->nCol*sizeof(char *))) ==(char **)NULL) || ((t->colIsNum= (int *)realloc((void *)t->colIsNum,t->nCol*sizeof(int))) ==(int *)NULL) || ((t->value =(double **)realloc((void *)t->value, t->nCol*sizeof(double *)))==(double **)NULL) || ((t->text = (char ***)realloc((void *)t->text, t->nCol*sizeof(char **))) ==(char ***)NULL) ) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } t->colIsNum[n] = TRUE; t->text[n] = (char **)NULL; if( ((t->name[n]=(char *)calloc(strlen(name)+32,sizeof(char)))==(char *)NULL) || ((t->unit[n]=(char *)calloc(strlen(unit)+32,sizeof(char)))==(char *)NULL) || ((t->value[n]=(double *)calloc(t->nRow,sizeof(double)))==(double *)NULL) ) { fprintf(stderr,"Cannot allocate memory.\n"); exit(1); } strcpy(t->name[n],name); strcpy(t->unit[n],unit); } /* appendColumn() */