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