
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "list.h"
#include "defs.h"
#include "util.h"
#include "init.h"
#include "func.h"

void
data_init_globals() {
	int	i;
	GROUPDEF	*gd;

	LastZoneID = 0;
	LastGroupID = 0;
	LastTableID = 0;
	TransfersSpecified = FALSE;

	zonelist = (LIST_PTR *)NULL;
	tablelist = (LIST_PTR *)NULL;
	grouplist = (LIST_PTR *)NULL;
	blocklist = (LIST_PTR *)NULL;
	plan_horizon=MAX_PLAN_HORIZON;	/* needed to fake model into creating zone0->priority[] */

	/* create overall zone */
	zone0 = (ZONE *)init_make_zone("0");

	candidatelist = NULL;
	age_offset = 0;
	plan_horizon = 0;
	harvest_rule = RELATIVE_OLDEST_FIRST;
	enable_randomization = FALSE;
	EnableClassSplit=TRUE;
	EnableReports=TRUE;
	EnableReportHeaders=TRUE;
	MinSplitThreshold = 0.0;
	for(i=0;i<MAX_PLAN_HORIZON;i++) {
		period_length[i] = 0;
	}
	sprintf(OutputDirectory, "reports");
}

void data_read_report_flags(char *datadir) {
	FILE *fp;
	ZONE *zone;
	GROUP *group;
	char fname[255], dataline[255], UnitTypeStr[20], UnitIDStr[30];
	int line_no, count, inv_ha_rep_start, inv_ha_rep_end, inv_ha_sum_start, inv_ha_sum_end,
		inv_m3_rep_start, inv_m3_rep_end, inv_m3_sum_start, inv_m3_sum_end,
		levels_rep_start, levels_rep_end, hage_rep_start, hage_rep_end, axs_rep_start, axs_rep_end;

	sprintf(fname, "%s/reports.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if ( NULL == fp ) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "UnitType");

	line_no = 1;
	while ( !feof(fp) ) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		count = sscanf(dataline, "%s %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
			UnitTypeStr, UnitIDStr,
			&inv_ha_rep_start, &inv_ha_rep_end,
			&inv_m3_rep_start, &inv_m3_rep_end,
			&inv_ha_sum_start, &inv_ha_sum_end,
			&inv_m3_sum_start, &inv_m3_sum_end,
			&axs_rep_start, &axs_rep_end,
			&levels_rep_start, &levels_rep_end,
			&hage_rep_start, &hage_rep_end);
		if ( count != 16) {
			if (count!=-1) {
				printf("\nWARNING: %s(Line#:%d): Discarding invalid dataline", fname, line_no);
			}
		} else {
			sprintf(UnitTypeStr, "%s", util_to_lower(UnitTypeStr));
			if (strcmp(UnitTypeStr, "zone")==0) {
				zone = (ZONE *)func_find_zone(UnitIDStr);
				if (zone == NULL) {
					printf("\n\nERROR: %s(Line#:%d): Could not find specified zone(%s)\n\n", fname, line_no, UnitIDStr);
					exit(-2);
				}
				if ( zone->rflags == NULL ) {
					zone->rflags = (REPORT_FLAGS *)malloc( sizeof(REPORT_FLAGS) );
					if ( zone->rflags == NULL ) {
						printf("\n\nERROR: data_read_report_flags(): malloc() failed\n\n");
						exit(-2);
					}
					zone->rflags->inv_start = MAX_PLAN_HORIZON;
					zone->rflags->inv_end = 0;
				}
				zone->rflags->inv_ha_rep_start = inv_ha_rep_start;
				zone->rflags->inv_ha_rep_end = inv_ha_rep_end;
				zone->rflags->inv_ha_sum_start = inv_ha_sum_start;
				zone->rflags->inv_ha_sum_end = inv_ha_sum_end;
				zone->rflags->inv_m3_rep_start = inv_m3_rep_start;
				zone->rflags->inv_m3_rep_end = inv_m3_rep_end;
				zone->rflags->inv_m3_sum_start = inv_m3_sum_start;
				zone->rflags->inv_m3_sum_end = inv_m3_sum_end;
				zone->rflags->hage_rep_start = hage_rep_start;
				zone->rflags->hage_rep_end = hage_rep_end;
				zone->rflags->levels_rep_start = levels_rep_start;
				zone->rflags->levels_rep_end = levels_rep_end;
				zone->rflags->axs_rep_start = axs_rep_start;
				zone->rflags->axs_rep_end = axs_rep_end;
				if ( inv_ha_rep_start < zone->rflags->inv_start ) zone->rflags->inv_start = inv_ha_rep_start;
				if ( inv_m3_rep_start < zone->rflags->inv_start ) zone->rflags->inv_start = inv_m3_rep_start;
				if ( inv_ha_sum_start < zone->rflags->inv_start ) zone->rflags->inv_start = inv_ha_sum_start;
				if ( inv_m3_sum_start < zone->rflags->inv_start ) zone->rflags->inv_start = inv_m3_sum_start;
				if ( inv_ha_rep_end > zone->rflags->inv_end ) zone->rflags->inv_end = inv_ha_rep_end;
				if ( inv_m3_rep_end > zone->rflags->inv_end ) zone->rflags->inv_end = inv_m3_rep_end;
				if ( inv_ha_sum_end > zone->rflags->inv_end ) zone->rflags->inv_end = inv_ha_sum_end;
				if ( inv_m3_sum_end > zone->rflags->inv_end ) zone->rflags->inv_end = inv_m3_sum_end;
			} else if (strcmp(UnitTypeStr, "group")==0) {
				group = (GROUP *)func_find_group(UnitIDStr);
				if (group == NULL) {
					printf("\n\nERROR: %s(Line#:%d): Could not find specified group(%s)\n\n", fname, line_no, UnitIDStr);
					exit(-2);
				}
				if ( group->rflags == NULL ) {
					group->rflags = (REPORT_FLAGS *)malloc( sizeof(REPORT_FLAGS) );
					if ( group->rflags == NULL ) {
						printf("\n\nERROR: data_read_report_flags(): malloc() failed\n\n");
						exit(-2);
					}
					group->rflags->inv_start = MAX_PLAN_HORIZON;
					group->rflags->inv_end = 0;
				}
				group->rflags->inv_ha_rep_start = inv_ha_rep_start;
				group->rflags->inv_ha_rep_end = inv_ha_rep_end;
				group->rflags->inv_ha_sum_start = inv_ha_sum_start;
				group->rflags->inv_ha_sum_end = inv_ha_sum_end;
				group->rflags->inv_m3_rep_start = inv_m3_rep_start;
				group->rflags->inv_m3_rep_end = inv_m3_rep_end;
				group->rflags->inv_m3_sum_start = inv_m3_sum_start;
				group->rflags->inv_m3_sum_end = inv_m3_sum_end;
				group->rflags->hage_rep_start = hage_rep_start;
				group->rflags->hage_rep_end = hage_rep_end;
				group->rflags->levels_rep_start = levels_rep_start;
				group->rflags->levels_rep_end = levels_rep_end;
				group->rflags->axs_rep_start = axs_rep_start;
				group->rflags->axs_rep_end = axs_rep_end;
				if ( inv_ha_rep_start < group->rflags->inv_start ) group->rflags->inv_start = inv_ha_rep_start;
				if ( inv_m3_rep_start < group->rflags->inv_start ) group->rflags->inv_start = inv_m3_rep_start;
				if ( inv_ha_sum_start < group->rflags->inv_start ) group->rflags->inv_start = inv_ha_sum_start;
				if ( inv_m3_sum_start < group->rflags->inv_start ) group->rflags->inv_start = inv_m3_sum_start;
				if ( inv_ha_rep_end > group->rflags->inv_end ) group->rflags->inv_end = inv_ha_rep_end;
				if ( inv_m3_rep_end > group->rflags->inv_end ) group->rflags->inv_end = inv_m3_rep_end;
				if ( inv_ha_sum_end > group->rflags->inv_end ) group->rflags->inv_end = inv_ha_sum_end;
				if ( inv_m3_sum_end > group->rflags->inv_end ) group->rflags->inv_end = inv_m3_sum_end;
			} else {
				printf("\n\nERROR: %s(Line#:%d): Invalid unit type specification(%s); expecting [ZONE,GROUP]\n\n", fname, line_no, UnitTypeStr);
				exit(-2);
			}
		}
	}

	fclose(fp);
}

void
data_read_neighbours(char *datadir) {
	FILE	*fp;
	BLOCK	*Block1, *Block2;
	char	fname[255], dataline[255], inBlock1[255], inBlock2[255];
	int	line_no, count;

	sprintf(fname, "%s/neighbours.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp==NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Block");

	line_no = 1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		count = sscanf(dataline, "%s %s", inBlock1, inBlock2);
		if (count!=2) {
			if (count!=-1) {
				printf("\nWARNING: %s(Line#:%d): Discarding invalid dataline", line_no);
			}
		} else {
			Block1 = (BLOCK *)func_find_block(inBlock1);
			if (Block1==NULL) {
				printf("\n\nERROR: %s(Line#:%d): Could not find specified block(%s)\n\n", fname,line_no,inBlock1);
				exit(-2);
			}
			Block2 = (BLOCK *)func_find_block(inBlock2);
			if (Block2==NULL) {
				printf("\n\nERROR: %s(Line#:%d): Could not find specified block(%s)\n\n", fname,line_no,inBlock2);
				exit(-2);
			}
			Block1->Neighbours = (LIST_PTR *)list_insert(Block1->Neighbours, Block2, NULL);
		}
	}
	fclose(fp);
}

void
data_read_transfer(char *datadir) {
	FILE	*fp;
	GROUP	*group;
	TRANSFER	*transfer;
	char	fname[255], dataline[255];
	int	line_no,count,inage,inctype,inrage,instart,inend;
	char	inzonestr[255],inaustr[255],inagestr[255],inctypestr[255],inragestr[255],ingroupstr[255];

	sprintf(fname, "%s/transfer.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp==NULL) {
		printf("\nWARNING: Could not open %s", fname);
		TransfersSpecified = FALSE;
		return;
	}
	TransfersSpecified = TRUE;
	fp = (FILE *)util_discard_header(fp, fname, "Group");

	line_no = 1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		line_no++;
		count = sscanf(dataline, "%s %d %d %s %s %s %s %s",
            /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition
               a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
            /*&ingroupstr, &instart, &inend, &inzonestr, &inaustr, &inagestr, &inctypestr, &inragestr);*/
            ingroupstr, &instart, &inend, inzonestr, inaustr, inagestr, inctypestr, inragestr);
		if (count!=8) {
			if (count!=-1) {
				printf("\nWARNING: %s(Line#:%d): Discarding invalid dataline", fname, line_no);
			}
		} else {
			if (strlen(ingroupstr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Group label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			sprintf(inagestr, "%s", util_to_lower(inagestr));
			sprintf(inctypestr, "%s", util_to_lower(inctypestr));
			sprintf(inragestr, "%s", util_to_lower(inragestr));

			group = (GROUP *)func_find_group(ingroupstr);
			if (group==NULL) {
				printf("\n\nERROR: %s(Line#:%d): Could not find specified group(%s)\n\n", fname, line_no, ingroupstr);
				exit(-2);
			}
			if ((instart<=0)||(instart>MAX_PLAN_HORIZON)) {
				printf("\n\nERROR: %s(Line#:%d): Specified Start Period(%d) outside allowed range(%d,%d)\n\n", fname, line_no, instart, 1, MAX_PLAN_HORIZON);
				exit(-2);
			}
			if ((inend<instart)||(inend>MAX_PLAN_HORIZON)) {
				printf("\n\nERROR: %s(Line#:%d): Specified End Period(%d) outside allowed range(%d,%d)\n\n", fname, line_no, inend, instart, MAX_PLAN_HORIZON);
				exit(-2);
			}

			if ((strcmp(inagestr, util_to_lower(NULLSTR))==0)) {
				inage = DUMMYINPUT;
			} else {
				inage = func_derive_integer(inagestr);
				if (inage==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid Age value(%s)\n\n", fname, line_no, inagestr);
					exit(-2);
				}
			}

			if ((strcmp(inctypestr, util_to_lower(NULLSTR))==0)) {
				inctype = DUMMYINPUT;
			} else {
				if (strcmp(inctypestr, "o")==0) {
					inctype = OPERABLE;
				} else if (strcmp(inctypestr, "i")==0) {
					inctype = INOPERABLE;
				} else if (strcmp(inctypestr, "d")==0) {
					inctype = DEFERRED;
				} else if (strcmp(inctypestr, "n")==0) {
					inctype = NONCONTRIBUTING;
				} else {
					printf("\n\nERROR: %s(Line#:%d): Invalid ClassType value(%s)\n\n", fname, line_no, inctypestr);
					exit(-2);
				}
			}

			if ((strcmp(inragestr, util_to_lower(NULLSTR))==0)) {
				inrage = DUMMYINPUT;
			} else {
				inrage = func_derive_integer(inragestr);
				if (inrage==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid Reversion Age value(%s)\n\n", fname, line_no, inragestr);
					exit(-2);
				}
			}

			init_make_transfer(group, instart, inend, inzonestr, inaustr, inage, inctype, inrage);
		}
	}

	fclose(fp);
}

void
data_read_priorities(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], inzonestr[255], inaustr[255], ingroupstr[255];
	ZONE		*zone;
	AU		*au;
	GROUP		*group;
	int		instart, inend, inprio, c, got_au, line_no;

	sprintf(fname, "%s/priority.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Zone");

	line_no=1;
	while (!feof(fp)) {
		c = (int)fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition*/
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!!*/
        /*c = sscanf(dataline, "%s %s %s %d %d %d", &inzonestr, &inaustr, &ingroupstr, &instart, &inend, &inprio);*/
        c = sscanf(dataline, "%s %s %s %d %d %d", inzonestr, inaustr, ingroupstr, &instart, &inend, &inprio);
		if (c != 6) {
			if (c != -1) {
				printf("\nWARNING: %s(Line#:%d): Fields read: %d; Discarding invalid dataline", fname, line_no, c);
			}
		} else {
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(ingroupstr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Group label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			/* validate data */
			if ((instart<=0)||(instart>MAX_PLAN_HORIZON)) {
				printf("\n\nERROR: %s(Line#:%d): Specified period(%d) outside allowed range (%d, %d)\n\n", fname, line_no, instart, 1, MAX_PLAN_HORIZON);
				exit(-2);
			}
			if ((inend<instart)||(inend>MAX_PLAN_HORIZON)) {
				printf("\n\nERROR: %s(Line#:%d): Specified period(%d) outside allowed range (%d, %d)\n\n", fname, line_no, inend, instart, MAX_PLAN_HORIZON);
				exit(-2);
			}
			if ((inprio<MIN_PRIORITY)||(inprio>MAX_PRIORITY)) {
				printf("\n\nERROR: %s(Line#:%d): Specified priority(%d) outside allowed range (%d, %d)\n\n", fname, line_no, inprio, MIN_PRIORITY, MAX_PRIORITY);
				exit(-2);
			}

			if (!strcmp(inzonestr,NULLSTR)==0) {
				if (!strcmp(ingroupstr,NULLSTR)==0) {
					printf("\n\nERROR: %s(Line#:%d): Cannot specify zone(%s) and group(%s) in same record\n\n", fname, line_no, inzonestr, ingroupstr);
					exit(-2);
				}
				zone = (ZONE *)func_find_zone(inzonestr);
				if (zone == NULL) {
					printf("\n\nERROR: %s(Line#:%d): Could not find specified zone (%s)\n\n", fname, line_no, inzonestr);
					exit(-2);
				}
				if (!strcmp(inaustr,NULLSTR)==0) {
					au = (AU *)func_find_au(zone, inaustr);
					if (au == NULL) {
						printf("\n\nERROR: %s(Line#:%d): Could not find specified zone/au (%s,%s)\n\n", fname, line_no, inzonestr, inaustr);
						exit(-2);
					}
					for(c=instart;c<=inend;c++) {
						if (c<=plan_horizon) au->priority[c] = inprio;
					}
				} else {
					for(c=instart;c<=inend;c++) {
						if (c<=plan_horizon) zone->priority[c] = inprio;
					}
				}
			} else {
				if (!strcmp(inaustr,NULLSTR)==0) {
					if (!strcmp(ingroupstr,NULLSTR)==0) {
						printf("\n\nERROR: %s(Line#:%d): Cannot specify AU(%s) and group(%s) in same record\n\n", fname, line_no, inaustr, ingroupstr);
						exit(-2);
					}
					got_au = FALSE;
					zonelist = (LIST_PTR *)list_reset(zonelist);
					zone = (ZONE *)list_next(zonelist);
					while (zone != NULL) {
						au = (AU *)func_find_au(zone, inaustr);
						if (au!=NULL) {
							got_au = TRUE;
							for(c=instart;c<=inend;c++) {
								if (c<=plan_horizon) au->priority[c] = inprio;
							}
						}
						zone = (ZONE *)list_next(zonelist);
					}
					if (got_au == FALSE) {
						printf("\n\nERROR: %s(Line#:%d): Could not find specified AU(%s)\n\n", fname, line_no, inaustr);
						exit(-2);
					}
				} else {
					if (strcmp(ingroupstr,NULLSTR)==0) {
						printf("\n\nERROR: %s(Line#:%d): No landunit specified; must specify at least one of zone/AU/group\n\n", fname, line_no);
						exit(-2);
					}
					group = (GROUP *)func_find_group(ingroupstr);
					if (group == NULL) {
						printf("\n\nERROR: %s(Line#:%d): Could not find specified group (%s)\n\n", fname, line_no, ingroupstr);
						exit(-2);
					}
					for(c=instart;c<=inend;c++) {
						if (c<=plan_horizon) group->priority[c] = inprio;
					}
				}
			}
		}
	}
	fclose(fp);
}

void
data_implement_landoaf() {
	ZONE	*zone;
	AU	*au;
	CLASS	*class;

	zonelist = (LIST_PTR *)list_reset(zonelist);
	zone = (ZONE *)list_next(zonelist);
	while (zone != NULL) {
		zone->aulist = (LIST_PTR *)list_reset(zone->aulist);
		au = (AU *)list_next(zone->aulist);
		while (au != NULL) {
			au->classlist = (LIST_PTR *)list_reset(au->classlist);
			class = (CLASS *)list_next(au->classlist);
			while (class != NULL) {
				class->area = class->area * au->landoaf;
				class = (CLASS *)list_next(au->classlist);
			}
			au = (AU *)list_next(zone->aulist);
		}
		zone = (ZONE *)list_next(zonelist);
	}
}

void
data_read_landoaf(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], inzonestr[255], inaustr[255];
	ZONE		*zone;
	AU		*au;
	int		found_au, count, line_no;
	double		inoaf;

	sprintf(fname, "%s/landoaf.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Zone");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		memset(inzonestr, '\0', sizeof(inzonestr));
		memset(inaustr, '\0', sizeof(inaustr));
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition */
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
        /*count = sscanf(dataline, "%s %s %lf", &inzonestr, &inaustr, &inoaf);*/
        count = sscanf(dataline, "%s %s %lf", inzonestr, inaustr, &inoaf);
		if (count != 3) {
			if (count != -1) {
				printf("\nWARNING: data_read_landoaf(Line#:%d): Fields read: %d; Discarding invalid dataline", line_no, count);
			}
		} else {
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			/* validate data */
			if (inoaf < 0.0) {
				printf("\n\nERROR: data_read_landoaf(Line#:%d): Landbase OAF < 0.0 for zone/au (%s,%s)\n\n", line_no, inzonestr, inaustr);
				exit(-2);
			}

			if ((strcmp(inzonestr, NULLSTR)==0)) {
				if ((strcmp(inaustr, NULLSTR)==0)) {
					/* ZONE NULL, AU NULL */
					zonelist = (LIST_PTR *)list_reset(zonelist);
					zone = (ZONE *)list_next(zonelist);
					while (zone != NULL) {
						zone->aulist = (LIST_PTR *)list_reset(zone->aulist);
						au = (AU *)list_next(zone->aulist);
						while (au != NULL) {
							if (au->landoaf != 1.00) {
								printf("\nFYI: data_read_landoaf(): Multiple landbase OAFs (%5.2lf, %5.2lf) applied to zone/AU (%s, %s); using %5.2lf", au->landoaf, inoaf, zone->label, au->label, inoaf);
							}
							au->landoaf = inoaf;
							au = (AU *)list_next(zone->aulist);
						}
						zone = (ZONE *)list_next(zonelist);
					}
				} else {
					/* ZONE NULL, AU SPECIFIED */
					found_au = FALSE;
					zonelist = (LIST_PTR *)list_reset(zonelist);
					zone = (ZONE *)list_next(zonelist);
					while (zone != NULL) {
						au = (AU *)func_find_au(zone, inaustr);
						if (au!=NULL) {
							found_au = TRUE;
							if (au->landoaf!=1.00) {
								printf("\nFYI: data_read_landoaf(): Multiple landbase OAFs (%5.2lf, %5.2lf) applied to zone/AU (%s, %s); using %5.2lf", au->landoaf, inoaf, zone->label, au->label, inoaf);
							}
							au->landoaf = inoaf;
						}
						zone = (ZONE *)list_next(zonelist);
					}
					if (found_au == FALSE) {
						printf("\n\nERROR: data_read_landoaf(Line#:%d): Could not find specified AU(%s)\n\n", line_no, inaustr);
						exit(-2);
					}
				}
			} else {
				zone = (ZONE *)func_find_zone(inzonestr);
				if (zone==NULL) {
					printf("\n\nERROR: data_read_landoaf(Line#:%d): Could not find specified zone(%s)\n\n", line_no, inzonestr);
					exit(-2);
				}
				if ((strcmp(inaustr, NULLSTR)==0)) {
					/* ZONE SPECIFIED, AU NULL */
					zone->aulist = (LIST_PTR *)list_reset(zone->aulist);
					au = (AU *)list_next(zone->aulist);
					while (au != NULL) {
						if (au->landoaf != 1.00) {
							printf("\nFYI: data_read_landoaf(): Multiple landbase OAFs (%5.2lf, %5.2lf) applied to zone/AU (%s, %s); using %5.2lf", au->landoaf, inoaf, zone->label, au->label, inoaf);
						}
						au->landoaf = inoaf;
						au = (AU *)list_next(zone->aulist);
					}
				} else {
					/* ZONE SPECIFIED, AU SPECIFIED */
					au = (AU *)func_find_au(zone, inaustr);
					if (au == NULL) {
						printf("\n\nERROR: data_read_landoaf(Line#:%d): Could not find specified zone/au (%s,%s)\n\n", line_no, inzonestr, inaustr);
						exit(-2);
					}
					if (au->landoaf != 1.00) {
						printf("\nFYI: data_read_landoaf(): Multiple landbase OAFs (%5.2lf, %5.2lf) applied to zone/AU (%s, %s); using %5.2lf", au->landoaf, inoaf, zone->label, au->label, inoaf);
					}
					au->landoaf = inoaf;
				}
			}
		}
	}
	fclose(fp);
}


void
data_check_regen() {
	ZONE	*zone;
	AU	*au;
	REGEN	*regen;
	double	acc;

	zonelist = (LIST_PTR *)list_reset(zonelist);
	zone = (ZONE *)list_next(zonelist);
	while (zone != NULL) {
		zone->aulist = (LIST_PTR *)list_reset(zone->aulist);
		au = (AU *)list_next(zone->aulist);
		while (au != NULL) {
			/* If no regen, create dummy regen; simplifies processing later */
			if (au->regenlist == NULL) {
				acc = 100.0;
				regen = (REGEN *)init_make_regen(au, au, 0, 100.0, 0, 0);
			} else {
				/* Check that regen percentages add up to 100% (within double tolerances) */
				acc = 0.0;
				au->regenlist = (LIST_PTR *)list_reset(au->regenlist);
				regen = (REGEN *)list_next(au->regenlist);
				while (regen != NULL) {
					acc += regen->pc;
					regen = (REGEN *)list_next(au->regenlist);
				}
				if ((acc<100.0-ROUND_ERROR)||(acc>100.0+ROUND_ERROR)) {
					printf("\n\nERROR: data_check_regen(): Regeneration percentages for zone/au (%s,%s) do not add up to 100%%\n\n", zone->label, au->label);
					exit(-2);
				}
			}
			au = (AU *)list_next(zone->aulist);
		}
		zone = (ZONE *)list_next(zonelist);
	}
}

void
data_init_existing_roads() {
	ZONE	*zone;
	AU	*au;
	CLASS	*class;

	zonelist = (LIST_PTR *)list_reset(zonelist);
	zone = (ZONE *)list_next(zonelist);
	while (zone!=NULL) {
		zone->aulist = (LIST_PTR *)list_reset(zone->aulist);
		au = (AU *)list_next(zone->aulist);
		while (au!=NULL) {
			if (au->RoadRec!=NULL) {
				au->classlist = (LIST_PTR *)list_reset(au->classlist);
				class = (CLASS *)list_next(au->classlist);
				while (class!=NULL) {
					if (class->age<au->RoadRec->age) {
						class->roaded=TRUE;
					}
					class = (CLASS *)list_next(au->classlist);
				}
			}
			au = (AU *)list_next(zone->aulist);
		}
		zone = (ZONE *)list_next(zonelist);
	}
}

void
data_read_roads(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255],inzonestr[255],inaustr[255],
			inraustr[255],inreentrystr[255],intoagestr[255],inoffsetstr[255];
	int		count, line_no, inage, intoage, inreentry, inoffset;
	double		inpc;
	ZONE		*zone;
	AU		*au;
	ROADREC		*r;

	sprintf(fname, "%s/road.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Zone");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		count = sscanf(dataline, "%s %s %d %lf %s %s %s %s",
			inzonestr,inaustr,&inage,&inpc,inraustr,intoagestr,inoffsetstr,inreentrystr);
		if (count != 8) {
			printf("\nWARNING: data_read_road(Line#:%d): Discarding invalid dataline", line_no);
		} else {
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inraustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Regen AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			zone = (ZONE *)func_find_zone(inzonestr);
			if (zone == NULL) {
				printf("\n\nERROR: data_read_road(Line#:%d): Could not find/make specified zone(%s)\n\n", line_no, inzonestr);
				exit(-2);
			}
			au = (AU *)func_find_au(zone, inaustr);
			if (au == NULL) {
				printf("\n\nERROR: data_read_road(Line#:%d): Could not find/make specified zone/au(%s,%s)\n\n", line_no, inzonestr, inaustr);
				exit(-2);
			}
			sprintf(intoagestr, "%s", util_to_lower(intoagestr));
			if ((strcmp(intoagestr, "KEEP")==0)||(strcmp(intoagestr, "keep")==0)) {
				intoage = -INF;
			} else {
				intoage = func_derive_integer(intoagestr);
				if (intoage==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid RegenAge value(%s)\n\n", fname, line_no, intoagestr);
					exit(-2);
				}
			}
			inoffset = func_derive_integer(inoffsetstr);
			if (inoffset==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Offset value(%s)\n\n", fname, line_no, inoffsetstr);
				exit(-2);
			}
			inreentry = func_derive_integer(inreentrystr);
			if (inreentry==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid ReEntry value(%s)\n\n", fname, line_no, inreentrystr);
				exit(-2);
			}
			r = (ROADREC *)init_make_roadrec(zone, au, inage, inpc, intoage, inraustr, inoffset, inreentry);
			if (r==NULL) {
				printf("\n\nERROR: data_read_road(Line#:%d): Could not allocate new ROADREC structure\n\n", line_no);
				exit(-2);
			}
		}
	}
	fclose(fp);
}

void
data_read_regen(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], intoagestr[255], inzonestr[255],
			inaustr[255], inraustr[255],inreentrystr[255], inoffsetstr[255];
	int		count, intoage, line_no, inreentry, inoffset;
	double		inrpc;
	ZONE		*zone, *rzone;
	AU		*au, *rau;
	REGEN		*regen;

	sprintf(fname, "%s/regen.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Zone");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		memset(inzonestr, '\0', sizeof(inzonestr));
		memset(inaustr, '\0', sizeof(inaustr));
		memset(inraustr, '\0', sizeof(inraustr));
        if (count != 7)
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition*/
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!!*/
        /*count = sscanf(dataline, "%s %s %lf %s %s %s %s",*/
	    /*    inzonestr, inaustr, &inrpc, inraustr, intoagestr, inoffsetstr, &inreentrystr);*/
        count = sscanf(dataline, "%s %s %lf %s %s %s %s",
            inzonestr, inaustr, &inrpc, inraustr, intoagestr, inoffsetstr, inreentrystr);
		if (count != 7) {
			printf("\nWARNING: data_read_regen(Line#:%d): Discarding invalid dataline", line_no);
		} else {
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inraustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Regen AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			zone = (ZONE *)func_find_zone(inzonestr);
			if (zone == NULL) {
				printf("\n\nERROR: data_read_regen(Line#:%d): Could not find/make specified zone(%s)\n\n", line_no, inzonestr);
				exit(-2);
			}
			au = (AU *)func_find_au(zone, inaustr);
			if (au == NULL) {
				printf("\n\nERROR: data_read_regen(Line#:%d): Could not find/make specified zone/au(%s,%s)\n\n", line_no, inzonestr, inaustr);
				exit(-2);
			}
			rau = (AU *)func_find_au(zone, inraustr);
			if (rau == NULL) {
				printf("\n\nERROR: data_read_regen(Line#:%d): Could not find/make specified zone/au(%s,%s)\n\n", line_no, inzonestr, inraustr);
				exit(-2);
			}
			if ((strcmp(intoagestr, "KEEP")==0)||(strcmp(intoagestr, "keep")==0)) {
				intoage = -INF;
			} else {
				intoage = func_derive_integer(intoagestr);
				if (intoage==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid RegenAge value(%s)\n\n", fname, line_no, intoagestr);
					exit(-2);
				}
			}
			inoffset = func_derive_integer(inoffsetstr);
			if (inoffset==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Offset value(%s)\n\n", fname, line_no, inoffsetstr);
				exit(-2);
			}
			inreentry = func_derive_integer(inreentrystr);
			if (inreentry==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid ReEntryLag value(%s)\n\n", fname, line_no, inreentrystr);
				exit(-2);
			}
			regen = (REGEN *)init_make_regen(au, rau, intoage, inrpc, inoffset, inreentry);
		}
	}
	fclose(fp);
}

void
data_check_targets() {
	if (zone0->target==NULL) {
		printf("\n\nERROR: data_check_targets(): No targets specified for zone0\n\n");
		exit(-2);
	}
}

void
data_read_target(char *datadir) {
	FILE 		*fp;
	ZONE		*zone;
	GROUP		*group;
	char		fname[255], dataline[255], unittype[255], inunit[255],
			inminareastr[255], inmaxareastr[255], inminvolstr[255], inmaxvolstr[255];
	int		instart, inend, count, line_no, i, unit;
	double		inminarea, inmaxarea, inminvol, inmaxvol;
	double		period_minarea, period_maxarea, period_minvol, period_maxvol;
	TARGET		*t;

	sprintf(fname, "%s/targets.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\n\nERROR: Could not open %s\n\n", fname);
		exit(-2);
	}
	fp = (FILE *)util_discard_header(fp, fname, "UnitType");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition */
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
        /*count = sscanf(dataline, "%s %s %d %d %s %s %s %s", */
        /*    unittype, inunit, &instart, &inend, &inminareastr, &inmaxareastr, &inminvolstr, &inmaxvolstr); */
        count = sscanf(dataline, "%s %s %d %d %s %s %s %s",
            unittype, inunit, &instart, &inend, inminareastr, inmaxareastr, inminvolstr, inmaxvolstr);
		if (count != 8) {
			if (count != -1) {
				printf("\nWARNING: data_read_target(Line#:%d): Fields read: %d; Discarding invalid dataline", line_no, count);
			}
		} else {
			if (strlen(inunit)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Unit label length exceeds max label length (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			/* cast to lower case for ease of processing */
			sprintf(unittype, "%s", util_to_lower(unittype));

			if ((strcmp(inminareastr, util_to_lower(NULLSTR))==0)) {
				inminarea = (double)DUMMYINPUT;
			} else {
				inminarea = func_derive_float(inminareastr);
				if (inminarea==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid MinArea value(%s)\n\n", fname, line_no, inminareastr);
					exit(-2);
				}
			}
			if ((strcmp(inmaxareastr, util_to_lower(NULLSTR))==0)) {
				inmaxarea = (double)DUMMYINPUT;
			} else {
				inmaxarea = func_derive_float(inmaxareastr);
				if (inmaxarea==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid MaxArea value(%s)\n\n", fname, line_no, inmaxareastr);
					exit(-2);
				}
			}
			if ((strcmp(inminvolstr, util_to_lower(NULLSTR))==0)) {
				inminvol = (double)DUMMYINPUT;
			} else {
				inminvol = func_derive_float(inminvolstr);
				if (inminvol==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid MinVolume value(%s)\n\n", fname, line_no, inminvolstr);
					exit(-2);
				}
			}
			if ((strcmp(inmaxvolstr, util_to_lower(NULLSTR))==0)) {
				inmaxvol = (double)DUMMYINPUT;
			} else {
				inmaxvol = func_derive_float(inmaxvolstr);
				if (inmaxvol==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid MaxVolume value(%s)\n\n", fname, line_no, inmaxvolstr);
					exit(-2);
				}
			}

			/* validate */
			if (inend < instart) {
				printf("\n\nERROR: data_read_target(Line#:%d) Start Period (%d) > End Period (%d)", line_no, instart, inend);
				exit(-2);
			}
			if (instart>plan_horizon) {
				printf("\nWARNING: data_read_target(Line#:%d): Specified StartPeriod(%d)>PlanHorizon(%d); Setting StartPeriod=PlanHorizon", line_no, instart, plan_horizon);
				instart = plan_horizon;
			}
			if (inend>plan_horizon) {
				printf("\nWARNING: data_read_target(Line#:%d): Specified EndPeriod(%d)>PlanHorizon(%d); Setting EndPeriod=PlanHorizon", line_no, inend, plan_horizon);
				inend = plan_horizon;
			}

			if (strcmp(unittype, util_to_lower("ZONE"))==0) {
				zone = (ZONE *)func_find_zone(inunit);
				if (zone == NULL) {
					printf("\n\nERROR: data_read_target(Line#:%d): Could not find specified zone(%s)\n\n", line_no, inunit);
					exit(-2);
				}
				if (zone->target==NULL) {
					t = (TARGET *)init_make_target(zone, NULL, ZONETYPE, "data_read_target()");
				}
				unit = ZONETYPE;
			} else if (strcmp(unittype, util_to_lower("GROUP"))==0) {
				group = (GROUP *)func_find_group(inunit);
				if (group == NULL) {
					printf("\n\nERROR: data_read_target(Line#:%d): Could not find specified group(%s)\n\n", line_no, inunit);
					exit(-2);
				}
				if (group->target==NULL) {
					t = (TARGET *)init_make_target(NULL, group, GROUPTYPE, "data_read_target()");
				}
				unit = GROUPTYPE;
			} else {
				printf("\n\nERROR: data_read_target(Line#:%d): Invalid unit type specification\n\n", line_no);
				exit(-2);
			}

			for(i=instart;i<=inend;i++) {
				/* Adjust yearly to period target */
				if (inmaxvol != (double)DUMMYINPUT) {
					period_maxvol= inmaxvol * period_length[i];
				} else {
					period_maxvol= (double)DUMMYINPUT;
				}
				if (inminvol != (double)DUMMYINPUT) {
					period_minvol= inminvol * period_length[i];
				} else {
					period_minvol= (double)DUMMYINPUT;
				}
				if (inmaxarea != (double)DUMMYINPUT) {
					period_maxarea= inmaxarea * period_length[i];
				} else {
					period_maxarea= (double)DUMMYINPUT;
				}
				if (inminarea != (double)DUMMYINPUT) {
					period_minarea= inminarea * period_length[i];
				} else {
					period_minarea= (double)DUMMYINPUT;
				}
				if (unit==ZONETYPE) {
					zone->target->max_area[i] = period_maxarea;
					zone->target->min_area[i] = period_minarea;
					zone->target->max_volume[i] = period_maxvol;
					zone->target->min_volume[i] = period_minvol;
				} else if (unit==GROUPTYPE) {
					group->target->max_area[i] = period_maxarea;
					group->target->min_area[i] = period_minarea;
					group->target->max_volume[i] = period_maxvol;
					group->target->min_volume[i] = period_minvol;
				} else {
					printf("\n\nERROR: data_read_targets(): Unknown unit type (%s)\n\n", inunit);
					exit(-2);
				}
			}
		}
	}
	fclose(fp);
}

void
data_read_axs(char *datadir) {
	FILE 		*fp;
	ZONE		*zone;
	GROUP		*group;
	AXSDEF		*axs;
	char		fname[255], dataline[255], unittype[255],
			inAgestr[255], inPercentstr[255], inunit[255], inUnitsstr[255], inSignstr[255];
	double		Percent;
	int		Sign,active,Age,line_no,count,instart,inend;

	sprintf(fname, "%s/axs.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "UnitType");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		count = sscanf(dataline, "%s %s %d %d %s %s %s",
			unittype, inunit, &instart, &inend, inPercentstr, inSignstr, inAgestr);
		if (count != 7) {
			if (count != -1) {
				printf("\nWARNING: data_read_axs(Line#:%d): Fields read: %d; Discarding invalid dataline", line_no, count);
			}
		} else {
			if (strlen(inunit)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Unit label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (inend < instart) {
				printf("\n\nERROR: data_read_axs(Line#:%d) Start Period (%d) > End Period (%d)\n\n", line_no, instart, inend);
				exit(-2);
			}

			Percent = func_derive_float(inPercentstr);
			if (Percent==ERRORVAL) {
				printf("\n\nERROR: data_read_axs(Line#:%d): Invalid percentage(%s); expecting numeric\n\n", line_no, inPercentstr);
				exit(-2);
			}
			if (strcmp(inSignstr, ">=")==0) {
				Sign = AXS_GT;
			} else if (strcmp(inSignstr, "<")==0) {
				Sign = AXS_LT;
			} else {
				printf("\n\nERROR: data_read_axs(Line#:%d): Invalid sign(%s); expecting [<,>=]\n\n", line_no, inSignstr);
				exit(-2);
			}
			Age = func_derive_integer(inAgestr);
			if (Age==ERRORVAL) {
				printf("\n\nERROR: data_read_axs(Line#:%d): Invalid value(%s); expecting integer numeric\n\n", line_no, inAgestr);
				exit(-2);
			}

			sprintf(unittype, "%s", util_to_lower(unittype));
			if (strcmp(unittype, util_to_lower("ZONE"))==0) {
				zone = (ZONE *)func_find_zone(inunit);
				if (zone == NULL) {
					printf("\n\nERROR: data_read_axs(Line#:%d): Could not find specified zone(%s)\n\n", line_no, inunit);
					exit(-2);
				}
				axs = (AXSDEF *)init_make_axs(zone, NULL, ZONETYPE, instart, inend, Percent, Sign, Age);
			} else if (strcmp(unittype, util_to_lower("GROUP"))==0) {
				group = (GROUP *)func_find_group(inunit);
				if (group == NULL) {
					printf("\n\nERROR: data_read_axs(Line#:%d): Could not find specified group(%s)\n\n", line_no, inunit);
					exit(-2);
				}
				axs = (AXSDEF *)init_make_axs(NULL, group, GROUPTYPE, instart, inend, Percent, Sign, Age);
			} else {
				printf("\n\nERROR: data_read_axs(Line#:%d): Invalid unit type specification(%s); expecting [ZONE,GROUP]\n\n", line_no, unittype);
				exit(-2);
			}
		}
	}
	fclose(fp);
}

void
data_read_group(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], inagelowstr[255], inagehighstr[255], ingtype, inrtype,
			ingroupstr[255], inzonestr[255], inaustr[255], inctypestr[255], inblockstr[255];
	int		c, count, inagelow, inagehigh, gtype, ctype, line_no;
	GROUP		*group;
	GROUPDEF	*gd;
	ZONE	*zone;
	TABLE	*au;
	BLOCK	*block;

	sprintf(fname, "%s/group.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\nWARNING: Could not open %s", fname);
		return;
	}
	fp = (FILE *)util_discard_header(fp, fname, "Group");

	line_no=1;
	while (!feof(fp)) {
		c = (int)fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		if ( ( line_no % 1000 ) == 0 ) printf( "." );
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition */
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
        /*count = sscanf(dataline, "%s %c %s %s %s %s %s %s", */
        /*    &ingroupstr, &ingtype, &inzonestr, &inaustr, &inblockstr, &inagelowstr, &inagehighstr, &inctypestr); */
        count = sscanf(dataline, "%s %c %s %s %s %s %s %s",
            ingroupstr, &ingtype, inzonestr, inaustr, inblockstr, inagelowstr, inagehighstr, inctypestr);

		if (count != 8) {
			if (count != -1) {
				printf("\nWARNING: data_read_group(Line#:%d): Fields read: %d; Discarding invalid dataline", line_no, count);
			}
		} else  {
			if (strlen(ingroupstr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Group label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if ( strcmp( util_to_lower(inzonestr), NULLSTR) != 0 ) {
				zone = (ZONE *)func_find_zone(inzonestr);
				if ( NULL == zone ) {
					printf("\n\nERROR: %s(Line#:%d): Could not find zone(%s)\n\n", fname, line_no, inzonestr);
					exit(-2);
				}
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if ( strcmp( util_to_lower(inaustr), NULLSTR) != 0 ) {
				au = (TABLE *)func_find_table(inaustr);
				if ( NULL == au ) {
					printf("\n\nERROR: %s(Line#:%d): Could not find AU(%s)\n\n", fname, line_no, inaustr);
					exit(-2);
				}
			}
			if (strlen(inblockstr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Block label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			if ( strcmp( util_to_lower(inblockstr), NULLSTR) != 0 ) {
				block = (BLOCK *)func_find_block(inblockstr);
				if ( NULL == block ) {
					printf("\n\nERROR: %s(Line#:%d): Could not find Block(%s)\n\n", fname, line_no, inblockstr);
					exit(-2);
				}
			}
			/* cast to lower case to ease processing */
			sprintf(inagelowstr, "%s", util_to_lower(inagelowstr));
			sprintf(inagehighstr, "%s", util_to_lower(inagehighstr));
			sprintf(inctypestr, "%s", util_to_lower(inctypestr));
			ingtype = tolower(ingtype);

			/* derive land unit IDs */
			if (strcmp(ingroupstr, NULLSTR)==0) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Group ID(%s)n\n", fname, line_no, ingroupstr);
				exit(-2);
			}

			/* derive ages */
			if (strcmp(inagelowstr, util_to_lower(NULLSTR)) == 0) {
				inagelow = -INF;
			} else {
				inagelow = func_derive_integer(inagelowstr);
				if (inagelow==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid AgeLow value(%s)\n\n", fname, line_no, inagelowstr);
					exit(-2);
				}
			}
			if (strcmp(inagehighstr, util_to_lower(NULLSTR)) == 0) {
				inagehigh = INF;
			} else {
				inagehigh = func_derive_integer(inagehighstr);
				if (inagehigh==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid AgeHigh value(%s)\n\n", fname, line_no, inagehighstr);
					exit(-2);
				}
			}
			if (inagelow>inagehigh) {
				printf("\n\nERROR: data_read_group(Line#:%d): AgeLow(%d) > AgeHigh(%d)\n\n", line_no, inagelow, inagehigh);
				exit(-2);
			}

			/* group type */
			if (ingtype=='f') {
				gtype = FLOAT;
			} else if (ingtype=='s') {
				gtype = STATIC;
			} else {
				printf("\n\nERROR: %s(Line#:%d): Invalid group(%s) type specification(%c)\n\n", fname, line_no, ingroupstr, ingtype);
				exit(-2);
			}

			/* class type */
			if (strcmp(inctypestr, util_to_lower(NULLSTR))==0) {
				ctype = DUMMYINPUT;
			} else if (strlen(inctypestr)==1) {
				if (strcmp(inctypestr, "o")==0) {
					ctype = OPERABLE;
				} else if (strcmp(inctypestr, "i")==0) {
					ctype = INOPERABLE;
				} else if (strcmp(inctypestr, "d")==0) {
					ctype = DEFERRED;
				} else if (strcmp(inctypestr, "n")==0) {
					ctype = NONCONTRIBUTING;
				} else {
					printf("\n\nERROR: data_read_group(Line#:%d): Group(%s); Invalid type specification(%s)\n\n", line_no, ingroupstr, inctypestr);
					exit(-2);
				}
			} else {
				printf("\n\nERROR: data_read_group(Line#:%d): Group(%s); Invalid type specification(%s)\n\n", line_no, ingroupstr, inctypestr);
				exit(-2);
			}

			/* create group/group def record */
			group = (GROUP *)init_make_group(ingroupstr, gtype);
			gd = (GROUPDEF *)init_make_groupdef(group, inzonestr, inaustr, inblockstr, inagelow, inagehigh, ctype);
		}
	}
	fclose(fp);
}

void
data_implement_table_oafs() {
	TABLE	*table;
	int	i;
	double	fac;

	tablelist = (LIST_PTR *)list_reset(tablelist);
	table = (TABLE *)list_next(tablelist);
	while (table != NULL) {
		for(i=0;i<MAX_NUM_TABLE_VERTICES;i++) {
			/* volume oaf1 */
			table->Volume[i]=table->Volume[i]*table->voaf1;
			/* volume oaf2 */
			fac = ((100.0-(((table->voaf2*100.0)/table->RefAge)*i))/100.0);
			if (fac<0.0) {
				printf("\n\nERROR: Table(%s): Volume OAF2(%lf) at ReferenceAge(%d) results in negative volume at age(%d)\n\n", table->label, table->voaf2, table->RefAge, i);
				exit(-2);
			}
			table->Volume[i]=fac*table->Volume[i];
		}
		table = (TABLE *)list_next(tablelist);
	}
}

void
data_interpolate_tables() {
	TABLE	*table;
	int	i, j, c, age1, age2;
	double	vol1, vol2, vinc;

	tablelist = (LIST_PTR *)list_reset(tablelist);
	table = (TABLE *)list_next(tablelist);
	while (table != NULL) {
		/* Linearly interpolate between vertices */
		age1=0; vol1=table->Volume[age1];
		for(i=1;i<MAX_NUM_TABLE_VERTICES;i++) {
			if ((table->Volume[i]>DUMMYINPUT+ROUND_ERROR)||(table->Volume[i]<DUMMYINPUT-ROUND_ERROR)) {
				age2=i;vol2=table->Volume[i];
				vinc = (vol2-vol1)/(age2-age1);
				for(j=age1,c=0;j<=age2;j++,c++) {
					table->Volume[j] = table->Volume[age1]+(vinc*c);
				}
				age1=age2; vol1=vol2;
			}
		}
		for(i=age1;i<MAX_NUM_TABLE_VERTICES;i++) {
			table->Volume[i] = vol1;
		}

		table = (TABLE *)list_next(tablelist);
	}
}

void
data_read_oaf(char *datadir) {
	FILE	*fp;
	char	fname[255], dataline[255], id[255];
	int	mha, RefAge, count, line_no, AdjAge;
	double	voaf1, voaf2;
	TABLE	*table;

	sprintf(fname, "%s/oaf.dat", datadir);
	fp = fopen(fname, "r");
	if (fp == NULL) {
		printf("\n\nERROR: Could not open %s\n\n", fname);
		exit(-2);
	}
	fp = (FILE *)util_discard_header(fp, fname, "Table");

	line_no=1;
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		memset(id, '\0', sizeof(id));
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition */
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
        /*count = sscanf(dataline, "%s %d %d %d %lf %lf", &id, &mha, &AdjAge, &RefAge, &voaf1, &voaf2); */
        count = sscanf(dataline, "%s %d %d %d %lf %lf", id, &mha, &AdjAge, &RefAge, &voaf1, &voaf2);
		if (count < 6) {
			if (count != -1) {
				printf("\nWARNING: %s(Line#:%d): Fields read: %d; Discarding invalid dataline", fname, line_no, count);
			}
		} else {
			if (strlen(id)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Label width exceeds max label width (%d characters)\n\n", fname, line_no, MAX_LABEL_LENGTH);
				exit(-2);
			}
			table = (TABLE *)func_find_table(id);
			if (table == NULL) {
				printf("\n\nERROR: %s(Line#:%d): Could not find specified table(%s)\n\n", fname, line_no, id);
				exit(-2);
			}
			if (mha < 0) {
				printf("\n\nERROR: %s(Line#:%d): Table: %s;  Minimum harvest age (%d) < 0\n\n", fname, line_no, id, mha);
				exit(-2);
			} else {
				table->mha = mha;
			}
			if (AdjAge < 0) {
				printf("\n\nERROR: %s(Line#:%d): Table: %s;  Adjacency Age (%d) < 0\n\n", fname, line_no, id, AdjAge);
				exit(-2);
			} else {
				table->AdjacencyAge = AdjAge;
			}
			if (voaf1 < 0.0) {
				printf("\n\nERROR: %s(Line#:%d):  Table: %s; Volume OAF1(%5.1lf) < 0.0\n\n", fname, line_no, id, voaf1);
				exit(-2);
			} else {
				table->voaf1 = voaf1;
			}
			if (voaf2<0.0) {
				printf("\n\nERROR: %s(Line#:%d): Table: %s; Volume OAF2(%5.1lf) < 0.0\n\n", fname, line_no, id, voaf2);
				exit(-2);
			} else {
				table->voaf2 = voaf2;
			}
			if (RefAge<0) {
				printf("\n\nERROR: %s(Line#:%d): Table: %s; ReferenceAge<0\n\n", fname, line_no, id, RefAge);
				exit(-2);
			} else {
				table->RefAge=RefAge;
			}
		}
	}
	fclose(fp);
}

void
data_read_table(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], instr[255], *ptr, *tstr;
	char		inagestr[255], involstr[255];
	TABLE		*table;
	int		age, got_vol, i, line_no, c;
	double		vol;

	sprintf(fname, "%s/vols.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\n\nERROR: Could not open %s", fname);
		exit(-2);
	}

	line_no=1;
	while (!feof(fp)) {
		/* find and strip label */
		tstr = (char *)util_strip_label(fp, "Table:");
		if (tstr == NULL) break;
		if (strlen(tstr)>MAX_LABEL_LENGTH) {
			printf("\n\nERROR: %s: Table label (%s) width exceeds max label width (%d characters)\n\n", fname, tstr, MAX_LABEL_LENGTH);
			exit(-2);
		}

		/* create new table record */
		table = (TABLE *)init_make_table(tstr, "data_read_table()");

		fgets(instr, sizeof(instr), fp);	/* instr now holds header - discarded */
		got_vol = 1;
		while (got_vol) {
			fgets(instr, sizeof(instr), fp);
			if (feof(fp)) {
				got_vol = 0;
				break;
			}
			c = sscanf(instr, "%s %s", inagestr, involstr);
			if (c==2) {
				/* no action; expected number of fields */
			} else {
				got_vol = 0;
				break;
			}
			age = func_derive_integer(inagestr);
			if (age==ERRORVAL) {
				printf("\n\nERROR: %s(Table#:%s): Invalid Age value(%s)\n\n", fname, table->label, inagestr);
				exit(-2);
			}
			if ((age>MAX_NUM_TABLE_VERTICES)||(age<0)) {
				printf("\n\nERROR: data_read_table(): Invalid age");
				printf("\n\tTable: ...... %s", table->label);
				printf("\n\tAge: ........ %6d\n\n", age);
				exit(-2);
			}
			vol = func_derive_float(involstr);
			if (vol==ERRORVAL) {
				printf("\n\nERROR: %s(Table#:%s): Invalid Volume value(%s)\n\n", fname, table->label, involstr);
				exit(-2);
			}
			if (vol < 0.0) {
				printf("\n\nERROR: data_read_table(): Invalid volume");
				printf("\n\tTable: ...... %s", table->label);
				printf("\n\tAge: ........ %6d", age);
				printf("\n\tVolume: ..... %6.1lf\n\n", vol);
				exit(-2);
			}
			table->Volume[age] = vol;
		}
	}
	fclose(fp);
}

static void
data_read_class_error(int line_no, char *str) {
	printf("\n\nERROR: data_read_class(Line#:%d): %s\n\n", line_no, str);
	exit(-2);
}

void
data_read_class(char *datadir) {
	FILE		*fp;
	char		fname[255], dataline[255], inctype[255], errstr[255];
	int		inage, count, c, classtype, reversionage, inreversionage, line_no;
	double		inarea;
	ZONE		*zone;
	AU		*au;
	CLASS		*class;
	BLOCK		*block;
	char		inzonestr[255], inaustr[255], inblockstr[255], inagestr[255],
			inreversionagestr[255], inareastr[255], testblockstr[255];

	sprintf(fname, "%s/class.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		printf("\n\nERROR: Could not open %s\n\n", fname);
		exit(-2);
	}
	fp = (FILE *)util_discard_header(fp, fname, "Zone");


	line_no=1;
	while (!feof(fp)) {
		c = (int)fgets(dataline, sizeof(dataline), fp);
		line_no++;
		if (feof(fp)) {
			fclose(fp);
			return;
		}
		if ( ( line_no % 1000 ) == 0 ) printf( "." );
		inreversionage = -INF; classtype = DUMMYINPUT;
		memset(inzonestr, '\0', sizeof(inzonestr));
		memset(inaustr, '\0', sizeof(inaustr));
		memset(inblockstr, '\0', sizeof(inblockstr));
		memset(inagestr, '\0', sizeof(inagestr));
		memset(inareastr, '\0', sizeof(inareastr));
		memset(inctype, '\0', sizeof(inctype));
		memset(inreversionagestr, '\0', sizeof(inreversionagestr));
        /* WARNING!!! The following commented line works but is wrong in indirection: a char array name is by definition */
        /*  a pointer to its first element and, so, its name (not its address) should be passed to sscanf!!! */
        /*count = sscanf(dataline, "%s %s %s %s %s %s %s", */
        /*    &inzonestr, &inaustr, &inblockstr, &inagestr, &inareastr, &inctype, &inreversionagestr); */
        count = sscanf(dataline, "%s %s %s %s %s %s %s",
            inzonestr, inaustr, inblockstr, inagestr, inareastr, inctype, inreversionagestr);
		if (count<5) {
			if (count != -1) {
				printf("\nWARNING: %s(Line#:%d): Fields read: %d; Discarding invalid dataline", fname, line_no, count);
			}
		} else if ((count>=5)&&(count<=7)) {
			if (strlen(inzonestr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Zone label width exceeds max label width (%d characters)\n\n", fname, line_no,MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inaustr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): AU label width exceeds max label width (%d characters)\n\n", fname, line_no,MAX_LABEL_LENGTH);
				exit(-2);
			}
			if (strlen(inblockstr)>MAX_LABEL_LENGTH) {
				printf("\n\nERROR: %s(Line#:%d): Block label width exceeds max label width (%d characters)\n\n", fname, line_no,MAX_LABEL_LENGTH);
				exit(-2);
			}
			sprintf(testblockstr, "%s", util_to_lower(inblockstr));
			if (strcmp(testblockstr, "n/a")==0) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Block label (%s)\n\n", fname, line_no, inblockstr);
				exit(-2);
			}
			inage = func_derive_integer(inagestr);
			if (inage==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Age value(%s)\n\n", fname, line_no, inagestr);
				exit(-2);
			}
			inarea = func_derive_float(inareastr);
			if (inarea==ERRORVAL) {
				printf("\n\nERROR: %s(Line#:%d): Invalid Area value(%s)\n\n", fname, line_no, inareastr);
				exit(-2);
			}
			if ((int)strlen(inreversionagestr)>=1) {
				inreversionage = func_derive_integer(inreversionagestr);
				if (inreversionage==ERRORVAL) {
					printf("\n\nERROR: %s(Line#:%d): Invalid ReversionAge value(%s)\n\n", fname, line_no, inreversionagestr);
					exit(-2);
				}
			} else {
				inreversionage = -INF;
			}
			/* Class types */
			if ((int)(strlen(inctype)) < 1) {
				classtype = OPERABLE;
				reversionage = -INF;
			} else {
				if ((int)(strlen(inctype)) > 1) {
					sprintf(errstr, "Invalid class type string (%s); MUST be one of [O,o,I,i,D,d,N,n] or blank", inctype);
					data_read_class_error(line_no, errstr);
					return;
				}
				sprintf(inctype, "%s", util_to_lower(inctype));
				if (inctype[0]=='d') {
					if (count>=7) {
						classtype = DEFERRED;
						reversionage = inreversionage;
					} else {
						classtype = DEFERRED;
						data_read_class_error(line_no, "Must specify a REVERSION AGE for DEFERRED timber class types");
						return;
					}
				} else if (inctype[0]=='n') {
					if (count>=7) {
						classtype = NONCONTRIBUTING;
						reversionage = inreversionage;
					} else {
						classtype = NONCONTRIBUTING;
						data_read_class_error(line_no, "Must specify a REVERSION AGE for NON-CONTRIBUTING timber class types");
						return;
					}
				} else if (inctype[0]=='i') {
					classtype = INOPERABLE;
					reversionage = -INF;
				} else if (inctype[0]=='o') {
					classtype = OPERABLE;
					reversionage = -INF;
				} else if (count==5) {
					classtype = OPERABLE;
					reversionage = -INF;
				} else {
					data_read_class_error(line_no, "Invalid class type/reversion age");
					return;
				}
			}

			/* check for valid area */
			if (inarea<ROUND_ERROR) {
				data_read_class_error(line_no, "Invalid area value");
				return;
			}

			/* Reversion age; only applicable for deferred class types */
			if ((inreversionage!=-INF)&&((classtype==OPERABLE)||(classtype==INOPERABLE))) {
				data_read_class_error(line_no, "Cannot specify reversion age for OPERABLE/INOPERABLE timber class types");
				return;
			}

			zone = (ZONE *)init_make_zone(inzonestr);
			if ( NULL == zone ) {
				printf("\n\n%s(Line#:%d): init_make_zone() returned NULL\n\n", fname, line_no );
				exit(-2);
			}
			au = (AU *)func_find_au(zone, inaustr);
			if ( NULL == au ) {
				printf("\n\n%s(Line#:%d): func_find_au() returned NULL\n\n", fname, line_no );
				exit(-2);
			}
			inage += age_offset; /* adjust for initial age offset */
			if (inarea < 0.0) {
				data_read_class_error(line_no, "Invalid area value");
				return;
			}
			block = (BLOCK *)init_make_block(inblockstr);
			if (block==NULL) {
				data_read_class_error(line_no, "\n\nERROR: init_make_block() returned NULL\n\n");
				return;
			}
			class = (CLASS *)init_make_class(zone,
				au,
				block,		/* cutblock */
				inage,		/* age */
				1,		/* width; default to 1 at init */
				inarea,
				classtype,
				reversionage,
				0,		/* ReEntryLag */
				NULL,		/* floatgrouplist */
				NULL,		/* staticgrouplist */
				FALSE);		/* roaded */
		}
	}
	fclose(fp);
}

void
data_read_config(char *datadir) {
	FILE 	*fp;
	char	fname[255], *ptr, cmd[255];
	int	i, pl;

	sprintf(fname, "%s/config.dat", datadir);
	fp = (FILE *)fopen(fname, "r");
	if (fp == NULL) {
		harvest_rule = RELATIVE_OLDEST_FIRST;
		age_offset = 0;
		enable_randomization = FALSE;
		EnableClassSplit=TRUE;
		EnableTrace=FALSE;

		printf("\nWARNING: Could not open %s", fname);
		printf("\nFYI: Using FSSIM defaults");
		printf("\n\tHarvest Rule: ................ Relative Oldest First");
		printf("\n\tClass Splitting: ............. On");
		printf("\n\tClass Split Threshold: ....... %.2lf ha", 0.0);
		printf("\n\tInitial Age Offset: .......... 0 years");
		printf("\n\tCandidate Randomisation: ..... Off");
		printf("\n\tReports ...................... On");
		printf("\n\tReport Headers: .............. On");
		printf("\n\tTrace: ....................... Off");
		printf("\n\tOutput Directory: ............ %s", OutputDirectory);
		return;
	}

	/* extract harvest rule */
	rewind(fp);
	ptr = util_strip_label(fp, "Harvest Rule [1..4]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Harvest Rule field; using default(%d)", fname, RELATIVE_OLDEST_FIRST);
		harvest_rule = RELATIVE_OLDEST_FIRST;
	} else {
		harvest_rule = func_derive_integer(ptr);
		if (harvest_rule==ERRORVAL) {
			printf("\n\nERROR: %s: Invalid Harvest Rule value(%s)\n\n", fname, ptr);
			exit(-2);
		}
	}
	if (harvest_rule == ABSOLUTE_OLDEST_FIRST) {
		printf("\nFYI: Using harvest rule: ABSOLUTE OLDEST FIRST");
	} else if (harvest_rule == RELATIVE_OLDEST_FIRST) {
		printf("\nFYI: Using harvest rule: RELATIVE OLDEST FIRST");
	} else if (harvest_rule == ABSOLUTE_YOUNGEST_FIRST) {
		printf("\nFYI: Using harvest rule: ABSOLUTE YOUNGEST FIRST");
	} else if (harvest_rule == RANDOM) {
		printf("\nFYI: Using harvest rule: RANDOM");
	} else {
		printf("\n\nERROR: %s: Unknown harvest rule type(%s)\n\n", fname, ptr);
		exit(-2);
	}

	/* extract age offset */
	rewind(fp);
	ptr = util_strip_label(fp, "Initial Age Offset [years]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Initial Age Offset field; using default(%d)", fname, 0);
		age_offset = 0;
	} else {
		age_offset = func_derive_integer(ptr);
		if (age_offset==ERRORVAL) {
			printf("\n\nERROR: %s: Invalid Age Offset value(%s)\n\n", fname, ptr);
			exit(-2);
		}
	}
	printf("\nFYI: Offsetting initial tclass ages by %d years", age_offset);

	/* extract class splitting toggle */
	rewind(fp);
	ptr = util_strip_label(fp, "Class Splitting [On/Off]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Class Splitting field; using default(ON)", fname);
		EnableClassSplit = TRUE;
		printf("\nFYI: Class Splitting ENABLED");
	} else {
		if ((!strcmp(ptr, "On"))||(!strcmp(ptr,"on"))) {
			EnableClassSplit = TRUE;
			printf("\nFYI: Class Splitting ENABLED");
		} else if ((!strcmp(ptr, "Off"))||(!strcmp(ptr,"off"))) {
			EnableClassSplit = FALSE;
			printf("\nFYI: Class Splitting DISABLED");
		} else {
			printf("\nERROR: Class Splitting MUST be one of [On/Off]");
			printf("\n\tFilename: ............ %s", fname);
			printf("\n\tClass Splitting: ..... %s\n", ptr);
			exit(-2);
		}
	}

	/* extract class split threshold */
	if (EnableClassSplit==TRUE) {
		rewind(fp);
		ptr = util_strip_label(fp, "Class Split Threshold [ha]:");
		if (ptr == NULL) {
			MinSplitThreshold = 0.0;
			printf("\nWARNING: %s(): Could not find Class Split Threshold field; using default(%.2lf)", fname, MinSplitThreshold);
		} else {
			strcpy(cmd, ptr);
			MinSplitThreshold = func_derive_float(cmd);
			if (MinSplitThreshold==ERRORVAL) {
				printf("\n\nERROR: %s: Invalid MinSplitThreshold value(%s)\n\n", fname, cmd);
				exit(-2);
			}
		}
		printf("\nFYI: Class split threshold %.2lf ha", MinSplitThreshold);
	}

	/* extract trace toggle */
	rewind(fp);
	ptr = util_strip_label(fp, "Trace [On/Off]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Trace field; using default(FALSE)", fname);
		EnableTrace = FALSE;
		printf("\nFYI: Trace DISABLED");
	} else {
		if ((!strcmp(ptr, "On"))||(!strcmp(ptr,"on"))) {
			EnableTrace = TRUE;
			printf("\nFYI: Trace ENABLED");
		} else if ((!strcmp(ptr, "Off"))||(!strcmp(ptr,"off"))) {
			EnableTrace = FALSE;
			printf("\nFYI: Trace DISABLED");
		} else {
			printf("\n\nERROR: %s: Trace(%s) MUST be one of [On/Off]\n\n", fname, ptr);
			exit(-2);
		}
	}

	/* extract randomisation toggle */
	rewind(fp);
	ptr = util_strip_label(fp, "Randomizer [On/Off]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Randomizer field; using default(FALSE)", fname);
		enable_randomization = FALSE;
		printf("\nFYI: Randomization DISABLED");
	} else {
		if ((!strcmp(ptr, "On"))||(!strcmp(ptr,"on"))) {
			enable_randomization = TRUE;
			printf("\nFYI: Randomization ENABLED");
		} else if ((!strcmp(ptr, "Off"))||(!strcmp(ptr,"off"))) {
			enable_randomization = FALSE;
			printf("\nFYI: Randomization DISABLED");
		} else {
			printf("\nERROR: Randomization MUST be one of [On/Off]");
			printf("\n\tFilename: ........ %s", fname);
			printf("\n\tRandomization: ... %s\n", ptr);
			exit(-2);
		}
	}

	/* extract randomisation seed and seed random number generator */
	if (enable_randomization==TRUE) {
		rewind(fp);
		ptr = util_strip_label(fp, "Randomizer Seed [integer]:");
		if (ptr == NULL) {
			printf("\nFYI: Randomization Seed not specified: defaulting to 1");
			srand(1);
		} else {
			printf("\nFYI: Randomization Seed: %d", func_derive_integer(ptr));
			srand(func_derive_integer(ptr));
		}
	}

	/* extract reports toggle */
	rewind(fp);
	ptr = util_strip_label(fp, "Reports [On/Off]:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find Reports field; using default(ON)", fname);
		EnableReports = TRUE;
		printf("\nFYI: Reports ENABLED");
	} else {
		if ((!strcmp(ptr, "On"))||(!strcmp(ptr,"on"))) {
			EnableReports = TRUE;
			printf("\nFYI: Reports ENABLED");
		} else if ((!strcmp(ptr, "Off"))||(!strcmp(ptr,"off"))) {
			EnableReports = FALSE;
			printf("\nFYI: Reports DISABLED");
		} else {
			printf("\nERROR: Reports MUST be one of [On/Off]");
			printf("\n\tFilename: ...... %s", fname);
			printf("\n\tReports: ....... %s\n", ptr);
			exit(-2);
		}
	}

	/* extract report headers toggle */
	if (EnableReports==TRUE) {
		rewind(fp);
		ptr = util_strip_label(fp, "Report Headers [On/Off]:");
		if (ptr == NULL) {
			printf("\nWARNING: %s: Could not find Report Headers field; using default(ON)", fname);
			EnableReportHeaders = TRUE;
			printf("\nFYI: Report Headers ENABLED");
		} else {
			if ((!strcmp(ptr, "On"))||(!strcmp(ptr,"on"))) {
				EnableReportHeaders = TRUE;
				printf("\nFYI: Report Headers ENABLED");
			} else if ((!strcmp(ptr, "Off"))||(!strcmp(ptr,"off"))) {
				EnableReportHeaders = FALSE;
				printf("\nFYI: Report Headers DISABLED");
			} else {
				printf("\nERROR: Report Headers MUST be one of [On/Off]");
				printf("\n\tFilename: ............. %s", fname);
				printf("\n\tReport Headers: ....... %s\n", ptr);
				exit(-2);
			}
		}
	}

	/* extract output directory */
	rewind(fp);
	ptr = util_strip_label(fp, "Output Directory:");
	if (ptr == NULL) {
		sprintf(OutputDirectory, "reports");
		printf("\nWARNING: %s: Could not find Output Directory field; using default(%s)", fname, OutputDirectory);
	} else {
		sprintf(OutputDirectory, ptr);
	}
	printf("\nFYI: Output Directory: %s", OutputDirectory);

	/* extract LastPeriodTarget */
	rewind(fp);
	ptr = util_strip_label(fp, "LastPeriodTarget:");
	if (ptr == NULL) {
		printf("\nWARNING: %s: Could not find LastPeriodTarget field; using default(%d)", fname, MAX_PLAN_HORIZON+1);
		LastPeriodTarget = MAX_PLAN_HORIZON+1;
	} else {
		LastPeriodTarget = func_derive_integer(ptr);
		if (LastPeriodTarget==ERRORVAL) {
			printf("\n\nERROR: %s: Invalid LastPeriodTarget value(%s)\n\n", fname, ptr);
			exit(-2);
		}
		printf("\nFYI: LastPeriodTarget: %d", LastPeriodTarget);
	}

	/* extract GSPercentTarget  */
	if (LastPeriodTarget<=MAX_PLAN_HORIZON) {
		rewind(fp);
		ptr = util_strip_label(fp, "GSPercentTarget [%]:");
		if (ptr == NULL) {
			GSPercentTarget = DUMMYINPUT;
			printf("\nWARNING: %s: Could not find GSPercentTarget field; using default(%.2lf)", fname, GSPercentTarget);
		} else {
			strcpy(cmd, ptr);
			GSPercentTarget = func_derive_float(cmd);
			if (GSPercentTarget==ERRORVAL) {
				printf("\n\nERROR: %s: Invalid GSPercentTarget value(%s)\n\n", fname, cmd);
				exit(-2);
			}
			printf("\nFYI: GSPercentTarget: %.2lf%%", GSPercentTarget);
		}
	}

	printf("\n\n");
	fclose(fp);
}

void
data_read_time_parameters(char *datadir) {
	FILE	*fp;
	char	fname[255], dataline[255], *ptr;
	int	period, length, i, count, lookahead;

	sprintf(fname, "%s/param.dat", datadir);
	fp = fopen(fname, "r");
	if (fp == NULL) {
		printf("\n\nERROR: Could not open %s\n", fname);
		exit(-2);
	}

	/* extract planning horizon */
	ptr = util_strip_label(fp, "Plan Horizon:");
	if (ptr == NULL) {
		printf("\n\nERROR: %s: Plan Horizon MUST be specified\n\n", fname);
		exit(-2);
	}
	plan_horizon = func_derive_integer(ptr);
	if (plan_horizon==ERRORVAL) {
		printf("\n\nERROR: %s: Invalid Plan Horizon value(%s)\n\n", fname, ptr);
		exit(-2);
	}
	if ((plan_horizon < 1) || (plan_horizon > MAX_PLAN_HORIZON)) {
		printf("\n\nERROR: %s: Specified PlanHorizon(%d) outside allowable range(%d-%d)\n\n", fname, plan_horizon, 1, MAX_PLAN_HORIZON);
		exit(-2);
	}

	/* extract period length(s) */
	fp = util_discard_header(fp, fname, "Period");
	while (!feof(fp)) {
		fgets(dataline, sizeof(dataline), fp);
		count = sscanf(dataline, "%d %d", &period, &length);
		if (count != 2) {
			if (count != -1) {
				printf("\nWARNING: data_read_time_parameters(): Fields read: %d; Discarding invalid dataline", count);
			}
		} else {
			if ((period < 1) || (period > MAX_PLAN_HORIZON)) {
				printf("\n\nERROR: %s: Specified period(%d) outside allowable range(%d-%d)\n\n", fname, period, 1, MAX_PLAN_HORIZON);
				exit(-2);
			}

			if (length < 1) {
				printf("\n\nERROR: %s:  Invalid period length(%d)\n\n", fname, length);
				exit(-2);
			}

			for(i=period;i<=MAX_PLAN_HORIZON;i++) {
				period_length[i] = length;
			}
		}
	}

	fclose(fp);
}

