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 #ifndef __PLUGIN_REORDER_PARSEMAP_C__
8 #define __PLUGIN_REORDER_PARSEMAP_C__
9 
10 #ifndef MAIN_PLUGIN_REORDER
11 #define MAIN_PLUGIN_REORDER_PARSEMAP /* compile parseMap for command line testing */
12 #define DEBUG_PLUGIN_REORDER_PARSEMAP
13 #endif
14 
15 /*
16     Function:   REORDER_parseMap
17 
18     Author: 	Jay Brian Kummer/Medical College Of WI/Neitz & DeYoe Labs
19 
20     Date:   	April 21, 1997
21 
22     Purpose:
23 
24 		AFNI 'reorder' plugin routine that parses the epoch map file
25 		for a requested shuffling of a 3D+Time dataSet. This function
26 		will return an array of indices representing the new (reorderd)
27 		position of time-course values. The caller will then apply this
28 		order to all voxels in the dataSet.
29 
30 		This version of the parseMap function, 'parseMap', 'collates'
31 		duplicated classes in the map file in the order that they appear
32 		(e.g., given a sequence of classes [ D C A B A ], the reorderd
33 		order returned will be indices for [ A1 A2 B C D ]).
34 
35 		The "epoch map" is a series of entries (given in a text file)
36 		which indicates the classification and, implicitly, the target
37 		order of epochs of a time-course. These maps are companions to
38 		dataSets arising from specific sequences of stimulus presentation
39 		and, therefore, should have a one-to-one correspondence to those
40 		dataSets (i.e., they have the same time length).
41 
42 		The expected format of the epoch map file is as follows:
43 
44 			# Comment lines begin with a '#' and persist to the end of the line.
45 			[ <EpochClass><PointNumberInClass> | - ]
46 			...one entry for each TR in the stimulus presentation...
47 
48 		Each entry is either an 'EpochClass' or a '-'. The latter excludes the
49 		point from the resulting reordering. 'EpochClass' is a single letter,
50 		[a-zA-Z], which classifies the current epoch; the contatenated number
51 		is an increasing value from 1 to the epoch length and is used mainly
52 		to delimit contiguous instances of the same class.
53 
54 		For example:
55 
56 			# This map is a companion to a dataSet acquired during visual
57 			# presentation of sequence of different size rings:
58 			#
59 			#   RingSize |__A__
60 			#            |    |          __D__
61 			#            |    |__B__    |
62 			#            |         |    |
63 			#            |         |__C__
64 			#            |_____________________
65 			#            1                   20 (TR)
66 			#
67 			# 5 scans per presentation, total of 20 scans, 4 epochs.
68 			#
69 			# User expects response amplitudes to be proportional
70 			# to ring sizes; this will be much easier to analyze if
71 			# epochs are ordered corresponding to an increasing
72 			# trend in ring size, so reorder the epochs:
73 			C1
74 			C2
75 			C3
76 			C4
77 			C5
78 			B1
79 			B2
80 			B3
81 			B4
82 			B5
83 			D1
84 			D2
85 			D3
86 			D4
87 			D5
88 			A1
89 			A2
90 			A3
91 			A4
92 			A5
93 			# Resulting order:
94 			#   RingSize |               __A__
95 			#            |          __D__|
96 			#            |     __B__|
97 			#            |    |
98 			#            |__C_|
99 			#            |_____________________
100 			#            1                   20 (TR)
101 			#
102 
103 		The TR number of each entry is, implicitly, the line number of the
104 		entry; therefore, the number of lines in the map file (except for
105 		comment lines) should equal the number of TRs in the companion
106 		dataSet.
107 
108     Usage:
109 
110 		char *myFile = "QQ_epoch.map";	/  epoch map for a given experiment  /
111 		int length = npts;				/  number of time-course points in  /
112 										/  the dataSet  /
113 		ClassInfo *classKRH;				/  Place to store the sequence of classes /
114 		int classCount;					/  Number of classes in 'class' array /
115 		if(NULL == REORDER_parseMap(myFile, &length, &classKRH, &classCount)) {
116 			printf("!! error !!\n");
117 			FreeWorkspace();
118 			return NULL;
119 			}
120 
121     Input Parameters:
122 
123 		myFile, char *, pointer to a NULL-terminated string, filename for an
124 			epoch map
125 		length, int *, pointer to a value storing the number of time-course points
126 			in the dataSet
127     Output Parameters:
128 
129 		int *, pointer to an array of length 'length' containing indices (on
130 			the interval [0, (length-1)] for the new ordering of time-course
131 			data points. NULL is returned on any error.
132 		By parameter:
133 		class, ClassInfo **, address of a pointer to an array of structures
134 			that will contain class and class length info.
135 		classCount, integer, address of an integer to receive the length of 'class'
136 
137     Side Effects:
138 
139 		The contents of 'length' will hold the length of the 3D+time dataSet
140 		to be processed by this plugin; on return, this will contain the length of
141 		the array of indices returned by this function, which in turn will also be
142 		the time-length of the reorderd 3D+time dataset. The return length can only
143 		be altered (decreased) by excluding TR points from the reorderd dataSet (by
144 		specifying '-' in the epoch map file). 'class' will be allocated to the
145 		length 'classCount' if parsing is successful.
146 
147 	Pseudo Code:
148 
149 		Check input parameters.
150 		Count the number of entries in the file.
151 		...Pseudo code boring...losing consciousness...
152 
153 	Code: */
154 
155 #include <stdio.h>
156 #include <stdlib.h>
157 #include <ctype.h>
158 #include <string.h>
159 
160 #ifndef MAIN_PLUGIN_REORDER
161 typedef struct {
162 	char classKRH;
163 	int length;
164 	} ClassInfo;
165 #endif
166 
REORDER_parseMap(char * mapFile,int * length,ClassInfo ** classKRH,int * classCount)167 int *REORDER_parseMap(char *mapFile
168 					, int *length
169 					, ClassInfo **classKRH
170 					, int *classCount)
171 {
172 FILE *inf = NULL;
173 char c; int icc ;
174 char *sptr;
175 char *classList=NULL;
176 char cBuf[256] = {0};
177 int i;
178 int j;
179 int k=0;
180 int line;
181 int excluded;
182 int rawLength;
183 int *index = NULL;
184 int *classNum=NULL;
185 int classStart;
186 char currentClass;
187 int currentClassPos;
188 
189 /* Check input parameters */
190 if(NULL == mapFile) {
191 	printf("!! [AFNI/reorder] NULL file name !!\n");
192 	*length = *classCount = 0;
193 	return NULL;
194 	}
195 
196 if(0 == mapFile[0]) {
197 	printf("!! [AFNI/reorder] Empty file name !!\n");
198 	*length = *classCount = 0;
199 	return NULL;
200 	}
201 
202 if(NULL == (inf = fopen(mapFile, "r"))) {
203 	printf("!! [AFNI/reorder] Trouble opening '%s' for reading !!\n", mapFile);
204 	*length = *classCount = 0;
205 	return NULL;
206 	}
207 
208 /* Count the number of noncomment entries in the file */
209 for(i = 0, line = 1, excluded = 0;; line++) {
210 	if(i > *length) {
211 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
212 		printf("[parseMap] Entry count exceeds expected\n");
213 		#endif
214 		printf("!! [AFNI/reorder] Number of entries in 'mapFile' exceeds expected of %d !!\n"
215 			, *length);
216 		*length = *classCount = 0;
217 		return NULL;
218 		}
219 
220 	/* Test for EOF */
221     c = icc = fgetc(inf);
222 	if(EOF == icc || feof(inf)) {
223 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
224 		printf("[parseMap] EOF detected: ");
225 		#endif
226 		if(i != *length) {
227 			#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
228 			printf("Abnormal\n");
229 			#endif
230 			printf("!! [AFNI/reorder] Unexpected EOF at line %d !!\n", line);
231 			*length = *classCount = 0;
232 			return NULL;
233 			}
234 		else {
235 			#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
236 			printf("Normal\n");
237 			#endif
238 			break;
239 			}
240 		}
241 	ungetc(c, inf);
242 
243 	#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
244 	printf("[parseMap] Processing line %d...\n", line);
245 	#endif
246 
247 	/* Get the next line */
248 	fgets(cBuf, sizeof(cBuf), inf);
249 
250 	/* Delete newline character */
251 	sptr = strchr(cBuf, '\n');
252 	if(sptr) {
253 		*sptr = 0;
254 		}
255 
256 	/* Eat leading whitespace */
257 	sptr = cBuf;
258 	while(isspace(*sptr)) ++sptr;
259 
260 	/* Skip comments and empty lines */
261 	if('#' == *sptr || 0 == *sptr) {
262 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
263 		printf("[parseMap] Skipping comment or empty line.\n");
264 		#endif
265 		continue;
266 		}
267 
268 	if('-' == *sptr) { /* excluded value */
269 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
270 		printf("[parseMap] Excluded point detected.\n");
271 		#endif
272 		++i;
273 		++excluded;
274 		continue;
275 		}
276 
277 	/* Check for valid class name */
278 	if(!isalpha(*sptr)) {
279 		printf("!! [AFNI/reorder] Bad map entry: '%s' at line %d !!\n"
280 			, sptr, line);
281 		*length = *classCount = 0;
282 		return NULL;
283 		}
284 
285 	if(!isdigit(*(sptr+1))) {
286 		printf("!! [AFNI/reorder] Illformed entry '%s' at line %d !!\n"
287 			, sptr, line);
288 		*length = *classCount = 0;
289 		return NULL;
290 		}
291 
292 	++i;
293 	}
294 
295 /* Rewind the input file for reuse */
296 rewind(inf);
297 
298 /* Allocate workspace */
299 index = (int *)calloc(sizeof(int), i-excluded);
300 if(NULL == index) {
301 	printf("!! [AFNI/reorder] Allocation error(1) !!\n");
302 	*length = *classCount = 0;
303 	return NULL;
304 	}
305 
306 classList = (char *)calloc(sizeof(char), i);
307 if(NULL == classList) {
308 	printf("!! [AFNI/reorder] Allocation error(2) !!\n");
309 	free(index);
310 	*length = *classCount = 0;
311 	return NULL;
312 	}
313 
314 classNum = (int *)calloc(sizeof(int), i);
315 if(NULL == classNum) {
316 	printf("!! [AFNI/reorder] Allocation error(3) !!\n");
317 	free(index);
318 	free(classList);
319 	*length = *classCount = 0;
320 	return NULL;
321 	}
322 
323 /* Arrays to be reallocated (initialize) */
324 (*classKRH) = (ClassInfo *)calloc(sizeof(ClassInfo), 1);
325 if(NULL == (*classKRH)) {
326 	printf("!! [AFNI/reorder] Allocation error(4) !!\n");
327 	free(index);
328 	free(classList);
329 	free(classNum);
330 	*length = *classCount = 0;
331 	return NULL;
332 	}
333 
334 /* Set return length */
335 rawLength = i;
336 *length = i - excluded;
337 #ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
338 printf("[parseMap] Return length of array is %d elements...\n", *length);
339 #endif
340 
341 /* Collect mapping information from file */
342 currentClass = 0;
343 *classCount = 0;
344 for(i = 0, line = 1; i < rawLength; line++) {
345 	/* Get the next line */
346 	fgets(cBuf, sizeof(cBuf), inf);
347 
348 	/* Delete newline character */
349 	sptr = strchr(cBuf, '\n');
350 	if(sptr) {
351 		*sptr = 0;
352 		}
353 
354 	/* Eat leading whitespace */
355 	sptr = cBuf;
356 	while(isspace(*sptr)) ++sptr;
357 
358 	/* Skip comments and empty lines */
359 	if('#' == *sptr || 0 == *sptr) {
360 		continue;
361 		}
362 
363 	if('-' == *sptr) { /* excluded value */
364 		classList[i] = classNum[i] = 0;
365 		i++;
366 		continue;
367 		}
368 
369 	#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
370 	printf("[parseMap] Processing line %d [%s]...\n", line, sptr);
371 	#endif
372 
373 	classList[i] = cBuf[0];
374 	classNum[i] = atoi(&cBuf[1]);
375 
376 	/* Count classes and make sure they are numbered properly */
377 	if(0 == currentClass && 0 == *classCount) { /* first class */
378 		currentClass = cBuf[0];
379 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
380 		printf("[parseMap] First class: %c\n", currentClass);
381 		#endif
382 		k = classNum[i];
383 		if(k != 1) {
384 			printf("!! [AFNI/reorder] Invalid class numbering at line %d [Should start at 1] {1} !!\n"
385 				, line);
386 			free(classList);
387 			free(classNum);
388 			free(index);
389 			free((*classKRH));
390 			(*classKRH) = NULL;
391 			*length = *classCount = 0;
392 			return NULL;
393 			}
394 
395 		(*classCount)++;
396 
397 		/* reallocate space */
398 		(*classKRH) = (ClassInfo *)realloc((void *)(*classKRH), (*classCount)*sizeof(ClassInfo));
399 		if(NULL == (*classKRH)) {
400 			printf("!! [AFNI/reorder] Allocation error(4) !!\n");
401 			free(index);
402 			free(classList);
403 			free(classNum);
404 			*length = *classCount = 0;
405 			return NULL;
406 			}
407 		(*classKRH)[(*classCount)-1].classKRH = currentClass;
408 		(*classKRH)[(*classCount)-1].length = 1;
409 		}
410 	else if(currentClass != cBuf[0]) { /* new class */
411 		currentClass = cBuf[0];
412 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
413 		printf("[parseMap] Next class: %c\n", currentClass);
414 		#endif
415 		k = classNum[i];
416 		if(k != 1) {
417 			printf("!! [AFNI/reorder] Invalid class numbering at line %d [Should start at 1] {2} !!\n"
418 				, line);
419 			free(classList);
420 			free(classNum);
421 			free(index);
422 			free((*classKRH));
423 			(*classKRH) = NULL;
424 			*length = *classCount = 0;
425 			return NULL;
426 			}
427 
428 		(*classCount)++;
429 
430 		/* reallocate space */
431 		(*classKRH) = (ClassInfo *)realloc((void *)(*classKRH), (*classCount)*sizeof(ClassInfo));
432 		if(NULL == (*classKRH)) {
433 			printf("!! [AFNI/reorder] Allocation error(4) !!\n");
434 			free(index);
435 			free(classList);
436 			free(classNum);
437 			*length = *classCount = 0;
438 			return NULL;
439 			}
440 		(*classKRH)[(*classCount)-1].classKRH = currentClass;
441 		(*classKRH)[(*classCount)-1].length = 1;
442 		}
443 	else {
444 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
445 		printf("[parseMap] Next entry, checking class numbering...\n");
446 		#endif
447 		++k;
448 		if(1 == classNum[i]) { /* contiguous instance of current class */
449 			#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
450 			printf("[parseMap] Contiguous class: %c\n", currentClass);
451 			#endif
452 
453 			k = 1;
454 			(*classCount)++;
455 
456 			/* reallocate space */
457 			(*classKRH) = (ClassInfo *)realloc((void *)(*classKRH), (*classCount)*sizeof(ClassInfo));
458 			if(NULL == (*classKRH)) {
459 				printf("!! [AFNI/reorder] Allocation error(4) !!\n");
460 				free(index);
461 				free(classList);
462 				free(classNum);
463 				*length = *classCount = 0;
464 				return NULL;
465 				}
466 			(*classKRH)[(*classCount)-1].classKRH = currentClass;
467 			(*classKRH)[(*classCount)-1].length = 0;
468 			}
469 		else if(classNum[i] != k) {
470 			printf("!! [AFNI/reorder] Invalid class numbering at line %d {3} !!\n"
471 				, line);
472 			free(index);
473 			free(classList);
474 			free(classNum);
475 			free((*classKRH));
476 			(*classKRH) = NULL;
477 			*length = *classCount = 0;
478 			return NULL;
479 			}
480 		++(*classKRH)[(*classCount)-1].length;
481 		}
482 	++i;
483 	}
484 fclose(inf);
485 
486 #ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
487 printf("\n[parseMap] Epoch map has %d distinct classes.\n\n", *classCount);
488 printf("[parseMap] Parsed the following:\n");
489 for(i = 0; i < rawLength; i++) {
490 	if(classList[i]) {
491 		printf("  [%d] Class %c %d\n", i, classList[i], classNum[i]);
492 		}
493 	else {
494 		printf("  [%d] Excluded.\n", i);
495 		}
496 	}
497 printf("\n[parseMap] Meta-sequence of classes is:\n");
498 for(i = 0; i < *classCount; i++) {
499 	printf("  [%d] Class %c [Width %d TRs]\n", i, (*classKRH)[i].classKRH, (*classKRH)[i].length);
500 	}
501 #endif
502 
503 /* Build array of indices for reordering the time-course */
504 /* Find the first occurrence of the lowest value class [currentClass].
505    Store the index positions of that class sequence in the current
506    positions available in the 'index' array.
507 */
508 
509 #ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
510 printf("\n[parseMap] Building mapping array...\n");
511 #endif
512 for(i = 0, currentClass = 0; i < *length; /* quit when all indices are remapped */) {
513 	/* Get next class to remap */
514 	if(!currentClass) {
515 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
516 		printf("\n  Determining next class to map...\n");
517 		#endif
518 		j = 0;
519 		while(j < rawLength) { /* look for next non-null class */
520 			if(classList[j]) {
521 				break;
522 				}
523 			++j;
524 			}
525 		if(j == rawLength) {
526 			#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
527 			printf("  << No more classes to map >>\n");
528 			#endif
529 			break; /* done if there is none */
530 			}
531 
532 		currentClass = classList[j];	/* current epoch class to remap */
533 		classStart = classNum[j];	/* starting number of current class */
534 		currentClassPos = j;		/* index of current class */
535 
536 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
537 		printf("  Next valid class is '%c', checking to see if there is a better one...\n"
538 			, currentClass);
539 		#endif
540 
541 		/* See if there's a better one */
542 		for(j = 1; j < rawLength; j++) {
543 			if(classList[j]) {
544 				if(classList[j] < currentClass) {
545 					#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
546 					printf("    Class '%c' should be done before '%c'...\n"
547 						, classList[j], currentClass);
548 					#endif
549 					currentClass = classList[j];		/* new epoch class to remap */
550 					classStart = classNum[j];	/* starting number of new class */
551 					currentClassPos = j;		/* index of new class */
552 					}
553 				}
554 			}
555 
556 		#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
557 		printf("  Remapping class '%c' [starting index is %d]...\n"
558 			, currentClass, currentClassPos);
559 		#endif
560 		}
561 
562 	/* currenClassPos is the index of the start position for the current
563 	   class to be remapped; remap until class changes or class number
564 	   returns to 1 */
565 	/* for(k = currentClassPos; i < rawLength, k < rawLength; ) { */
566 	/* for(k = currentClassPos; i < rawLength && k < rawLength; ) { */
567 	   for(k = currentClassPos; k < rawLength; ) {
568 		if(classList[k]) {
569 			#ifdef DEBUG_PLUGIN_REORDER_PARSEMAP
570 			printf("    Index[%d] <-- %d [Old index]\n", i, k);
571 			#endif
572 			index[i] = k; /* store the position of the TR value from the old time-course */
573 			classList[k] = 0; /* mark as 'done' */
574 			++i;
575 			++k;
576 			}
577 		else { /* stop when a 'exclusion' point is found */
578 			break;
579 			}
580 
581 		if(classList[k] != currentClass || 1 == classNum[k]) {
582 			break;
583 			}
584 		}
585 	currentClass = 0;
586 	}
587 
588 /* Free workspace */
589 free(classNum);
590 
591 return(index);
592 }
593 
594 #ifdef MAIN_PLUGIN_REORDER_PARSEMAP
main(int argc,char * argv[])595 main(int argc, char *argv[])
596 {
597 int *index = NULL;
598 ClassInfo *classKRH = NULL;
599 int classCount = 0;
600 int length;
601 int i;
602 
603 if(3 != argc) {
604 	printf("usage: parseMap <MapFile> <TargetCount>\n");
605 	exit(1);
606 	}
607 
608 length = atoi(argv[2]);
609 if(length < 1) {
610 	printf("!! [Main] Invalid target count: %d !!\n", length);
611 	exit(1);
612 	}
613 
614 if(NULL == (index = REORDER_parseMap(argv[1], &length, &classKRH, &classCount))) {
615 	printf("!! [Main] Trouble parsing epoch map file !!\n");
616 	exit(1);
617 	}
618 
619 printf("\n[Main] Indices for epoch remapping:\n");
620 for(i = 0; i < length; i++) {
621 	printf("  %d\n", index[i]);
622 	}
623 
624 printf("\n[Main] Meta-sequence of classes is:\n");
625 for(i = 0; i < classCount; i++) {
626 	printf("  [%d] Class %c [Width %d TRs]\n", i, classKRH[i].classKRH, classKRH[i].length);
627 	}
628 
629 free(index);
630 free(classKRH);
631 }
632 #endif
633 
634 #endif
635 
636