1 /*****************************************************************************
2 Major portions of this software are copyrighted by the Medical College
3 of Wisconsin, 1994-2000, and are released under the Gnu General Public
4 License, Version 2. See the file README.Copyright for details.
5 ******************************************************************************/
6
7 #include "multivector.h"
8
9 /*********************************************************************
10 Routines to handle the multivector data type: a collection of
11 1D arrays of strings or floats, all the same length.
12
13 RWCox - May 1999
14 **********************************************************************/
15
16 /*-------------------------------------------------------------------
17 Check 2 strings for equivalence, regardless of case
18 ---------------------------------------------------------------------*/
19
my_strequiv(char * s1,char * s2)20 static int my_strequiv( char * s1 , char * s2 )
21 {
22 int ii , ll ;
23
24 if( s1 == NULL && s2 == NULL ) return 1 ;
25 if( s1 == NULL || s2 == NULL ) return 0 ;
26 ii = strlen(s1) ; ll = strlen(s2) ; if( ii != ll ) return 0 ;
27 for( ii=0 ; ii < ll ; ii++ )
28 if( toupper(s1[ii]) != toupper(s2[ii]) ) return 0 ;
29 return 1 ;
30 }
31
32 /*--------------------------------------------------------------------
33 Throw away a multivector
34 ----------------------------------------------------------------------*/
35
multivector_free(multivector * mv)36 void multivector_free( multivector * mv )
37 {
38 int ii ;
39
40 if( mv == NULL ) return ;
41
42 if( mv->name != NULL ) free(mv->name) ;
43 if( mv->type != NULL ) free(mv->type) ;
44 if( mv->label != NULL )
45 for( ii=0 ; ii < mv->nvec ; ii++ ) free(mv->label[ii]) ;
46 if( mv->vec != NULL )
47 for( ii=0 ; ii < mv->nvec ; ii++ ) free(mv->vec[ii]) ;
48
49 free(mv) ; return ;
50 }
51
52 /*-------------------------------------------------------------------
53 Read a multivector from disk
54 ---------------------------------------------------------------------*/
55
56 #define NVMAX 128
57 #define LBUF 2048
58 #define SEPCH " \t\n"
59
60 #define MERR(ss) \
61 fprintf(stderr,"*** multivector_read error; file=%s: %s\n",fname,ss)
62
multivector_read(char * fname)63 multivector * multivector_read( char * fname )
64 {
65 FILE * fp ;
66 char buf[LBUF] ;
67 char * ptr , * pp[NVMAX] ;
68 multivector * mv ;
69 int ii , ll , nvec,ndim , first=0 ;
70 float val ;
71
72 /*-- sanity check --*/
73
74 if( fname == NULL || fname[0] == '\0' ) return NULL ;
75
76 fp = fopen( fname , "r" ) ;
77 if( fp == NULL ){ MERR("can't open file"); return NULL; }
78
79 mv = (multivector *) malloc( sizeof(multivector) ) ;
80 nvec = ndim = mv->nvec = mv->ndim = 0 ;
81 mv->name = strdup(fname) ;
82 mv->type = NULL ; mv->label = NULL ; mv->vec = NULL ;
83
84 /*-- read and process any header comments --*/
85
86 while(1){
87 ptr = afni_fgets( buf , LBUF , fp ) ;
88 if( ptr == NULL ){
89 fclose(fp); multivector_free(mv); MERR("no data"); return NULL;
90 }
91
92 ll = strlen(buf) ;
93 for( ii=ll-1 ; ii >= 0 ; ii-- ) if( !isspace(buf[ii]) ) break ;
94 if( ii < 0 ) continue ; /* was all blanks; goto next line */
95
96 if( buf[0] != '#' ){ first=1; break; } /* not a header line ==> done */
97
98 ptr = strtok( buf , SEPCH ) ;
99
100 /* handle #NAME */
101
102 if( my_strequiv(ptr,"#NAME") ){
103 ptr = strtok( NULL , SEPCH ) ;
104 if( ptr != NULL ){
105 free(mv->name) ; mv->name = strdup(ptr) ;
106 }
107 continue ; /* goto next line */
108 }
109
110 /* handle #TYPE */
111
112 if( my_strequiv(ptr,"#TYPE") ){
113 int ntyp=0 , typ[NVMAX] ;
114
115 if( mv->type != NULL ){
116 fclose(fp); multivector_free(mv); MERR("second #TYPE"); return NULL;
117 }
118
119 /* scan tokens for type strings */
120
121 while(1){
122 ptr = strtok( NULL , SEPCH ) ;
123 if( ptr == NULL ) break ;
124
125 if( ntyp >= NVMAX ){
126 fclose(fp); multivector_free(mv); MERR("oversize #TYPE"); return NULL;
127 }
128
129 if( my_strequiv(ptr,"STRING") ) typ[ntyp++] = MV_STRING ;
130 else if( my_strequiv(ptr,"FLOAT") ) typ[ntyp++] = MV_FLOAT ;
131 else {
132 fclose(fp); multivector_free(mv); MERR("illegal #TYPE"); return NULL;
133 }
134 }
135
136 if( ntyp == 0 ){
137 fclose(fp); multivector_free(mv); MERR("illegal #TYPE"); return NULL;
138 }
139
140 if( mv->nvec > 0 && ntyp != mv->nvec ){
141 fclose(fp); multivector_free(mv); MERR("illegal #TYPE count"); return NULL;
142 }
143
144 if( mv->nvec == 0 ) nvec = mv->nvec = ntyp ;
145 mv->type = (int *) malloc( sizeof(int) * ntyp ) ;
146 for( ii=0 ; ii < ntyp ; ii++ ) mv->type[ii] = typ[ii] ;
147 continue ; /* goto next line */
148 }
149
150 /* handle #LABEL */
151
152 if( my_strequiv(ptr,"#LABEL") ){
153 int nlab=0 ; char * lab[NVMAX] ;
154
155 if( mv->label != NULL ){
156 fclose(fp); multivector_free(mv); MERR("second #LABEL"); return NULL;
157 }
158
159 /* scan tokens for label strings */
160
161 while(1){
162 ptr = strtok( NULL , SEPCH ) ;
163 if( ptr == NULL ) break ;
164
165 if( nlab >= NVMAX ){
166 for( ii=0 ; ii < nlab ; ii++ ) free( lab[ii] ) ;
167 fclose(fp); multivector_free(mv); MERR("oversize #LABEL"); return NULL;
168 }
169
170 lab[nlab++] = strdup(ptr) ;
171 }
172
173 if( nlab == 0 ){
174 fclose(fp); multivector_free(mv); MERR("illegal #LABEL"); return NULL;
175 }
176
177 if( mv->nvec > 0 && nlab != mv->nvec ){
178 for( ii=0 ; ii < nlab ; ii++ ) free( lab[ii] ) ;
179 fclose(fp); multivector_free(mv); MERR("illegal #LABEL count"); return NULL;
180 }
181
182 if( mv->nvec == 0 ) nvec = mv->nvec = nlab ;
183 mv->label = (char **) malloc( sizeof(char *) * nlab ) ;
184 for( ii=0 ; ii < nlab ; ii++ ) mv->label[ii] = lab[ii] ;
185 continue ; /* goto next line */
186 }
187
188 /* otherwise, just ignore the line (it's a comment, maybe) */
189
190 } /* end of scan over header lines */
191
192 /*-- read and store data lines --*/
193
194 while(1){
195 if( !first ) ptr = afni_fgets( buf , LBUF , fp ) ;
196 if( ptr == NULL ) break ; /* end of input */
197 first = 0 ;
198
199 ll = strlen(buf) ;
200 for( ii=ll-1 ; ii >= 0 ; ii-- ) if( !isspace(buf[ii]) ) break ;
201 if( ii < 0 ) continue ; /* was all blanks; goto next line */
202 if( buf[0] == '#' ) continue ; /* a comment line; goto next line */
203
204 /* extract tokens from this line */
205
206 pp[0] = strtok(buf,SEPCH) ; if( pp[0] == NULL ) continue ;
207 ll = 1 ;
208 while(1){
209 pp[ll] = strtok(NULL,SEPCH) ; if( pp[ll] == NULL ) break ;
210 ll++ ;
211 }
212
213 /* check count */
214
215 if( nvec == 0 ){
216 mv->nvec = nvec = ll ;
217 if( nvec > NVMAX ) MERR("too many columns") ;
218 }
219 if( ll > nvec ) ll = nvec ;
220
221 /* make type, if needed */
222
223 if( mv->type == NULL ){
224 mv->type = (int *) malloc( sizeof(int) * nvec ) ;
225 for( ii=0 ; ii < ll ; ii++ ){
226 val = strtod( pp[ii] , &ptr ) ;
227 if( *ptr != '\0' ) mv->type[ii] = MV_STRING ;
228 else mv->type[ii] = MV_FLOAT ;
229 }
230 for( ; ii < nvec ; ii++ ) /* this can only happen if #LABEL */
231 mv->type[ii] = MV_FLOAT ; /* is used and has too many labels */
232 }
233
234 /* initialize vector space, if needed */
235
236 if( mv->vec == NULL ){
237 mv->vec = (void **) malloc( sizeof(void *) * nvec ) ;
238 for( ii=0 ; ii < nvec ; ii++ )
239 mv->vec[ii] = (void *) malloc( sizeof(float)*16 ) ;
240 }
241
242 /* expand vector space for new row of data,
243 convert tokens to values and store them in this space */
244
245 for( ii=0 ; ii < nvec ; ii++ ){
246 switch( mv->type[ii] ){
247 case MV_FLOAT:{
248 float * fpt ;
249 mv->vec[ii] = (void *) realloc( mv->vec[ii], sizeof(float)*(ndim+1) );
250 fpt = (float *) mv->vec[ii] ;
251 fpt[ndim] = (ii < ll) ? strtod( pp[ii] , NULL ) : 0.0 ;
252 }
253 break ;
254
255 case MV_STRING:{
256 char ** cpt ;
257 mv->vec[ii] = (void *) realloc( mv->vec[ii], sizeof(char *)*(ndim+1) );
258 cpt = (char **) mv->vec[ii] ;
259 cpt[ndim] = (ii < ll) ? strdup(pp[ii]) : strdup("\0") ;
260 }
261 break ;
262 }
263 }
264 ndim++ ; /* just added a new element! */
265
266 } /* end of processing this line */
267
268 /*-- done --*/
269
270 mv->ndim = ndim ; return mv ;
271 }
272
273 /*-------------------------------------------------------------------
274 (Re)set the name stored in a multivector.
275 nname can be NULL t{o clear the name.
276 ---------------------------------------------------------------------*/
277
multivector_set_name(multivector * mv,char * nname)278 void multivector_set_name( multivector * mv , char * nname )
279 {
280 if( mv->name != NULL ){ free(mv->name); mv->name = NULL; }
281
282 if( nname != NULL ) mv->name = strdup(nname) ;
283 return ;
284 }
285
286
287 /*-------------------------------------------------------------------
288 Write a multivector to disk.
289 Returns 0 if it fails, 1 if it succeeds.
290 ---------------------------------------------------------------------*/
291
multivector_write(char * fname,multivector * mv)292 int multivector_write( char * fname , multivector * mv )
293 {
294 int nvec,ndim , ii,kk,ll , width[NVMAX] ;
295 char buf[LBUF] , fbuf[32] ;
296 FILE * fp ;
297 float * fpt ;
298 char ** cpt ;
299
300 /*-- sanity checks --*/
301
302 if( !THD_filename_ok(fname) || mv == NULL ) return 0 ;
303
304 nvec = mv->nvec ; ndim = mv->ndim ;
305 if( nvec < 1 || ndim < 1 ) return 0 ;
306
307 if( mv->type == NULL || mv->vec == NULL ) return 0 ;
308
309 /*-- open file, write headers --*/
310
311 if( strcmp(fname,"-") == 0 ){
312 fp = stdout ;
313 } else {
314 fp = fopen( fname , "w" ) ; if( fp == NULL ) return 0 ;
315 }
316
317 if( mv->name != NULL ) fprintf(fp,"#NAME %s\n",mv->name) ;
318
319 if( mv->label != NULL ){
320 sprintf(buf,"#LABEL") ;
321 for( ii=0 ; ii < nvec ; ii++ ){
322 ll = strlen(buf) ;
323 if( mv->label[ii] != NULL )
324 sprintf(buf+ll," %s",mv->label[ii]) ;
325 else
326 sprintf(buf+ll," -none-") ;
327 }
328 fprintf(fp,"%s\n",buf) ;
329 }
330
331 sprintf(buf,"#TYPE") ;
332 for( ii=0 ; ii < nvec ; ii++ ){
333 ll = strlen(buf) ;
334 switch( mv->type[ii] ){
335 case MV_FLOAT: sprintf(buf+ll," FLOAT" ) ; break ;
336 case MV_STRING: sprintf(buf+ll," STRING") ; break ;
337 }
338 width[ii] = 1 ;
339 }
340 fprintf(fp,"%s\n",buf) ;
341
342 /*-- scan vectors to determine maximum column widths --*/
343
344 for( kk=0 ; kk < ndim ; kk++ ){
345 for( ii=0 ; ii < nvec ; ii++ ){
346 switch( mv->type[ii] ){
347 case MV_FLOAT:
348 fpt = (float *) mv->vec[ii] ;
349 MV_fval_to_char( fpt[kk] , fbuf ) ; ll = strlen(fbuf) ;
350 width[ii] = MAX( width[ii] , ll ) ;
351 break ;
352
353 case MV_STRING:
354 cpt = (char **) mv->vec[ii] ; ll = strlen(cpt[kk]) ;
355 width[ii] = MAX( width[ii] , ll ) ;
356 break ;
357 }
358 }
359 }
360
361 /*-- write data in columns --*/
362
363 for( kk=0 ; kk < ndim ; kk++ ){
364 buf[0] = '\0' ;
365 for( ii=0 ; ii < nvec ; ii++ ){
366 ll = strlen(buf) ;
367 switch( mv->type[ii] ){
368 case MV_FLOAT:
369 fpt = (float *) mv->vec[ii] ;
370 MV_fval_to_char( fpt[kk] , fbuf ) ;
371 sprintf(buf+ll," %*s",width[ii],fbuf) ;
372 break ;
373
374 case MV_STRING:
375 cpt = (char **) mv->vec[ii] ;
376 sprintf(buf+ll," %*s",width[ii],cpt[kk]) ;
377 break ;
378 }
379 }
380 fprintf(fp,"%s\n",buf) ;
381 }
382
383 /*-- done --*/
384
385 if( fp != stdout ) fclose(fp) ;
386 return 1 ;
387 }
388
389 /*----------------------------------------------------------------
390 Adapted from AV_fval_to_char
391 ------------------------------------------------------------------*/
392
393 #define MV_NCOL 12
394
MV_fval_to_char(float qval,char * buf)395 void MV_fval_to_char( float qval , char *buf )
396 {
397 float aval = fabs(qval) ;
398 int lv ;
399 char lbuf[32] ;
400 int il ;
401
402 /* special case if the value is an integer */
403
404 if( qval == 0.0 ){ strcpy(buf,"0"); return; }
405
406 lv = (fabs(qval) < 99999999.0) ? (int)qval : 100000001 ;
407
408 if( qval == lv && abs(lv) < 100000000 ){
409 sprintf( buf, "%d" , lv ) ; return ;
410 }
411
412 /* macro to strip trailing zeros from output */
413
414 #undef BSTRIP
415 #define BSTRIP for( il=strlen(lbuf)-1 ; \
416 il>1 && (lbuf[il]=='0' || lbuf[il]==' ') ; \
417 il-- ) lbuf[il] = '\0'
418
419 /* noninteger: choose floating format based on magnitude */
420
421 lv = (int) (10.0001 + log10(aval)) ;
422
423 switch( lv ){
424
425 default:
426 if( qval > 0.0 ) sprintf( lbuf , "%-12.6e" , qval ) ;
427 else sprintf( lbuf , "%-12.5e" , qval ) ;
428 break ;
429
430 case 6: /* 0.0001-0.001 */
431 case 7: /* 0.001 -0.01 */
432 case 8: /* 0.01 -0.1 */
433 case 9: /* 0.1 -1 */
434 case 10: /* 1 -9.99 */
435 sprintf( lbuf , "%-9.6f" , qval ) ; BSTRIP ; break ;
436
437 case 11: /* 10-99.9 */
438 sprintf( lbuf , "%-9.5f" , qval ) ; BSTRIP ; break ;
439
440 case 12: /* 100-999.9 */
441 sprintf( lbuf , "%-9.4f" , qval ) ; BSTRIP ; break ;
442
443 case 13: /* 1000-9999.9 */
444 sprintf( lbuf , "%-9.3f" , qval ) ; BSTRIP ; break ;
445
446 case 14: /* 10000-99999.9 */
447 sprintf( lbuf , "%-9.2f" , qval ) ; BSTRIP ; break ;
448
449 case 15: /* 100000-999999.9 */
450 sprintf( lbuf , "%-9.1f" , qval ) ; BSTRIP ; break ;
451
452 case 16: /* 1000000-9999999.9 */
453 sprintf( lbuf , "%-9.0f" , qval ) ; break ;
454 }
455
456 strcpy(buf,lbuf) ; return ;
457 }
458
459 /*!
460 \sa MV_format_fval2 ZSS May 28 04
461 */
MV_format_fval(float fval)462 char * MV_format_fval( float fval )
463 {
464 static char buf[32] ;
465 MV_fval_to_char( fval , buf ) ;
466 return buf ;
467 }
468
469 /*!
470 \brief s = MV_format_fval2( fval, len);
471 same as fval, but will attempt to keep
472 the number len characters long. That's done
473 by truncating digits to the right of the decimal
474 point, if one exists.
475 \sa MV_fval_to_char
476 \sa MV_format_fval ZSS, RickR May 28 04
477 */
MV_format_fval2(float fval,int len)478 char * MV_format_fval2( float fval, int len)
479 {
480 static char bufm[10][32];
481 static int icall=0;
482 char *buf=NULL;
483 int wid;
484 char *pos = NULL;
485
486 ++icall;
487 if (icall > 9) icall = 0;
488 buf = (char *)bufm[icall]; /* This way we can make multiple calls
489 formatting function inside one *printf*
490 call ZSS Jan 2014 */
491
492 MV_fval_to_char( fval , buf ) ;
493 if (len < 1) return (buf);
494 if (strlen(buf) < len) return (buf);
495
496 /* trim it down */
497 pos = strchr (buf, 'e');
498 if (pos) return(buf); /* scientific notation, get out
499 (ZSS, thanks to tip by Ben Singer Dec 12 05) */
500 pos = strchr (buf, '.');
501 if (!pos) return(buf); /* can't do no'in */
502 wid = pos - buf;
503 if (wid < len) buf[len] = '\0';
504 if (buf[len-1] == '.') buf[len-1] = '\0'; /* remove trailing period */
505 return buf ;
506
507 }
508