%{
/****************************************************************************
 *                                                                          *
 *     Loki - Programs for genetic analysis of complex traits using MCMC    *
 *                                                                          *
 *             Simon Heath - University of Washington                       *
 *                                                                          *
 *                       July 1997                                          *
 *                                                                          *
 * param_parse.y:                                                           *
 *                                                                          *
 * yacc source for parameter file parser.                                   *
 *                                                                          *
 ****************************************************************************/

#include <stdlib.h>
#include <string.h>
#ifdef USE_DMALLOC
#include <dmalloc.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
	
#include "utils.h"
#include "loki_scan.h"
#include "loki_ibd.h"
#include "shared_peel.h"
#include "mat_utils.h"
#include "loki.h"
#include "ranlib.h"
#include "output_recomb.h"

#ifndef YYDEBUG
#define YYDEBUG 0
#endif
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 0
#endif
#ifndef __GNUC__
#define __GNUC__ 0
#endif

extern FILE *yyin;
double *residual_var,*residual_var_limit,*additive_var,*additive_var_limit,*grand_mean,limit_time,tloci_mean;
int *res_var_set,*add_var_set,*grand_mean_set,num_iter,max_tloci=16,min_tloci,sex_map,iflag;
int tloci_mean_set,start_tloci=-1,dump_freq,output_type=DEFAULT_OUTPUT_TYPE,limit_timer_type;
int sample_from[2]={0,0};
int sample_freq[2]={1,1};
static int start_flag=1,compress_ibd,ibd_mode;
static struct Marker *freq_marker;
static int check_variance(double,int);
static void set_position(struct lk_variable *, double, double);
static struct lk_variable *find_var(char *, int, int);
static struct Marker *check_marker(struct lk_variable *);
static int find_allele(char *, struct Marker *,int, int);
static int find_group(char *,int, int);
static int find_trait(struct lk_variable *);
static void set_output_gen(char *,char *);
static struct IBD_List *add_ibd_list(double,struct IBD_List *);
static void set_freq(struct Marker *, double,int);
static void set_map_range(char *,double,double, int);
static void set_tloci(int,int);	
static void set_ibd_list(char *,struct IBD_List *,int);
static void set_ibd_markers(char *);
static void set_output(struct lk_variable *);
static void set_group_order(int);
static void set_analyze(char *);
static void set_ibd_mode(char *);
static struct Variable *group_var;
static int group_ptr,*group_order,group_counter,freq_allele,c_flag;
extern void yyerror(char *s),print_scan_err(char *fmt, ...);
extern int yyparse(void),yylex(void),lineno,lineno1,tokenpos;
extern char *yytext,linebuf[];
char *Output_Phen,*Outputfile,*Dumpfile,*Freqfile,*Haplofile,*Polyfile;
char *OutputPosfile,*OutputIBDfile,*OutputIBDdir;

struct output_gen *Output_Gen;

static int max_scan_errors=30;
static int scan_warn_n,max_scan_warnings=30;
static int *ran_flag;
int scan_error_n,output_haplo;

struct id_data syst_var[NUM_SYSTEM_VAR];

%}

%union  {
	char *string;
	int value;
	double rvalue;
	struct IBD_List *rlist;
	struct lk_variable *lk_var;
}

%token RESIDUAL GENETIC VARIANCE POSITION FREQUENCY VIRTUAL
%token START MEAN ITERATIONS SAMPLE FROM OUTPUT MAP TOTAL
%token SEED SFILE SEEDFILE TRAIT LOCI SET SYSTEM_VAR TIMECOM
%token ESTIMATE IBD GROUP ORDER MALE FEMALE LIMIT AFFECTED
%token PHENO GENO COUNTS DUMP TYPE ANALYZE NORMAL STUDENT_T
%token HAPLO INCLUDE FUNCTION HALDANE KOSAMBI RECOMB POLYGENIC
%token MARKERS GRID COMPRESS DIR
  
%token <string> STRING
%token <value> INTEGER SYSTEM_VAR
%token <rvalue> REAL
%type <rvalue> rnum
%type <rlist> ibdlist
%type <lk_var> lkvar
%type <value> allele,group,trait_var

%%

parmfile: {lineno1=lineno;} command1 {iflag=0;}
       | error {iflag=0;}
       | parmfile {lineno1=lineno;} command1 {iflag=0;}
       | parmfile error {iflag=0;}
       ;

command1: command
       | command_a
       | START {start_flag=2;} command {start_flag=1;}
       ;

command: resvarcommand
       | addvarcommand
       | positioncommand
       | frequencycommand
       | meancommand
       ;

command_a: itercommand
       | samplecommand
       | outputcommand
       | mapcommand
       | seedcommand
       | tlocicommand
       | setcommand
       | ibdcommand
       | groupcommand
       | limitresvarcommand
       | limitaddvarcommand
       | analyzecommand
		 | includecommand
       | aff_freqcommand
       | limit_timecommand
		 | compresscommand
       ;

samplecommand: SAMPLE FROM INTEGER {sample_from[0]=sample_from[1]=$3;}
       | SAMPLE FROM INTEGER opt_comma INTEGER {sample_from[0]=$3; sample_from[1]=$5;}
       | START OUTPUT INTEGER {sample_from[0]=sample_from[1]=$3;}
       | START OUTPUT INTEGER opt_comma INTEGER {sample_from[0]=$3; sample_from[1]=$5;}
       ;

setcommand: SET SYSTEM_VAR INTEGER { syst_var[$2].data.value=$3; syst_var[$2].flag=ST_INTEGER; }
       | SET SYSTEM_VAR REAL { syst_var[$2].data.rvalue=$3; syst_var[$2].flag=ST_REAL; }
       | SET STRING rnum { free($2); yyerror("Unrecognized system variable"); }
       ;

includecommand: INCLUDE {iflag=1;} STRING {include_param_file($3);}
       ;

opt_comma:
       | ','
       ;

ibdcommand: ESTIMATE IBD STRING opt_comma ibdlist { set_ibd_list($3,$5,IBD_EST_DISCRETE); free($3);}
       | ESTIMATE IBD ibdlist { set_ibd_list(0,$3,IBD_EST_DISCRETE); }
       | ESTIMATE IBD MARKERS STRING { set_ibd_markers($4); free($4);}
       | ESTIMATE IBD MARKERS { set_ibd_markers(0); }
       | ESTIMATE IBD GRID STRING opt_comma ibdlist { set_ibd_list($4,$6,IBD_EST_GRID); free($4);}
       | ESTIMATE IBD GRID ibdlist { set_ibd_list(0,$4,IBD_EST_GRID); }
       ;

compresscommand: COMPRESS IBD OUTPUT {compress_ibd=1;}
		 | COMPRESS OUTPUT IBD {compress_ibd=1;}
		 ;
		 
aff_freqcommand: ESTIMATE AFFECTED FREQUENCY {est_aff_freq=1;}
       ;

analyzecom: STRING {set_analyze($1); free($1);}
       | AFFECTED {set_analyze("AFFECTED");}
       | IBD {set_analyze("IBD");}
       ;

analyzelist: analyzecom
       | analyzelist ',' analyzecom
       ;

analyzecommand: ANALYZE {analysis=0;} analyzelist
       ;

limit_timecommand: TIMECOM LIMIT rnum {limit_time=$3,limit_timer_type=ITIMER_REAL;}
       | LIMIT TIMECOM rnum {limit_time=$3,limit_timer_type=ITIMER_REAL;}
       | LIMIT VIRTUAL TIMECOM rnum {limit_time=$4,limit_timer_type=ITIMER_VIRTUAL;}
       | VIRTUAL TIMECOM LIMIT rnum {limit_time=$4,limit_timer_type=ITIMER_VIRTUAL;}
       ;

seedcommand: SEEDFILE STRING {if(Seedfile) free(Seedfile); Seedfile=$2;}
       | SEEDFILE STRING ',' INTEGER {if(Seedfile) free(Seedfile); Seedfile=$2; if($4) *ran_flag|=1; else *ran_flag&=~1;}
       | SEED SFILE STRING {if(Seedfile) free(Seedfile); Seedfile=$3;}
       | SEED SFILE STRING ',' INTEGER {if(Seedfile) free(Seedfile); Seedfile=$3; if($5) *ran_flag|=1; else *ran_flag&=~1;}
       | SEED INTEGER {
			 if($2<0) yyerror("Seedvalue out of range");
			 else {
				 init_ranf($2);
				 *ran_flag|=2;
			 }
		 }
       ;
mapcommand: MAP STRING rnum ',' rnum {set_map_range($2,$3,$5,-1); free($2);}
       | MALE MAP STRING rnum ',' rnum {set_map_range($3,$4,$6,X_PAT); free($3); }
       | FEMALE MAP STRING rnum ',' rnum {set_map_range($3,$4,$6,X_MAT); free($3); }
       | TOTAL MAP rnum {set_map_range(0,$3,$3,-1);}
       | TOTAL MAP rnum ',' rnum {set_map_range(0,$3,$5,-2);}
       | TOTAL MALE MAP rnum {set_map_range(0,$4,$4,X_PAT);}
       | TOTAL FEMALE MAP rnum {set_map_range(0,$4,$4,X_MAT);}
       | MAP TOTAL rnum {set_map_range(0,$3,$3,-1);}
       | MAP TOTAL rnum ',' rnum {set_map_range(0,$3,$5,-2);}
       | MALE MAP TOTAL rnum {set_map_range(0,$4,$4,X_PAT);}
       | FEMALE MAP TOTAL rnum {set_map_range(0,$4,$4,X_MAT);}
       | MAP FUNCTION HALDANE {map_function=MAP_HALDANE;}
       | MAP FUNCTION KOSAMBI {map_function=MAP_KOSAMBI;}
       ;

tlocicommand: TRAIT LOCI INTEGER {set_tloci(-1,$3);}
       | START TRAIT LOCI INTEGER {set_tloci(-2,$4);}
       | TRAIT LOCI INTEGER ',' INTEGER {set_tloci($3,$5);}
       | TRAIT LOCI MEAN rnum {tloci_mean=$4; tloci_mean_set=1;}
       ;

itercommand: ITERATIONS INTEGER { num_iter=$2; };

outputcommand: OUTPUT FREQUENCY INTEGER {sample_freq[0]=sample_freq[1]=$3;}
       | OUTPUT FREQUENCY INTEGER ',' INTEGER {sample_freq[0]=$3; sample_freq[1]=$5;}
       | OUTPUT FREQUENCY STRING {if(Freqfile) free(Freqfile); Freqfile=$3;}
       | SAMPLE FREQUENCY INTEGER {sample_freq[0]=sample_freq[1]=$3;}
       | SAMPLE FREQUENCY INTEGER ',' INTEGER {sample_freq[0]=$3; sample_freq[1]=$5;}
       | OUTPUT PHENO STRING {Output_Phen=$3; }
       | OUTPUT GENO STRING {set_output_gen($3,0);} 
       | OUTPUT GENO STRING ',' STRING {set_output_gen($3,$5); free($5); }
       | OUTPUT lkvarlist
       | OUTPUT TYPE INTEGER {output_type=$3;}
       | OUTPUT SFILE STRING {if(Outputfile) free(Outputfile); Outputfile=$3;}
       | OUTPUT POSITION SFILE STRING {if(OutputPosfile) free(OutputPosfile); OutputPosfile=$4;}
       | OUTPUT IBD SFILE STRING {if(OutputIBDfile) free(OutputIBDfile); OutputIBDfile=$4;}
       | OUTPUT IBD DIR STRING {if(OutputIBDdir) free(OutputIBDdir); OutputIBDdir=$4;}
       | DUMP SFILE STRING {if(Dumpfile) free(Dumpfile); Dumpfile=$3;}
       | DUMP FREQUENCY INTEGER {dump_freq=$3;}
		 | OUTPUT HAPLO STRING {output_haplo=1;if(Haplofile) free(Haplofile); Haplofile=$3;}
		 | OUTPUT HAPLO {output_haplo=1;}
		 | OUTPUT RECOMB STRING {output_recomb=1;if(Recombfile) free(Recombfile); Recombfile=$3;}
		 | OUTPUT POLYGENIC STRING {if(Polyfile) free(Polyfile); Polyfile=$3;}
		 | OUTPUT IBD STRING {set_ibd_mode($3); free($3);}
		 | OUTPUT IBD STRING ',' STRING {set_ibd_mode($3); free($3); set_ibd_mode($5); free($5);}
		 ;

resvarcommand: RESIDUAL VARIANCE rnum { if(!check_variance($3,0)) {res_var_set[0]=start_flag; residual_var[0]=$3;} }
       | RESIDUAL VARIANCE trait_var rnum { if($3>=0 && !check_variance($4,1)) {res_var_set[$3]=start_flag; BB(residual_var,$3,$3)=$4;} }
       ;

limitresvarcommand: RESIDUAL VARIANCE LIMIT rnum { if(!check_variance($4,0)) residual_var_limit[0]=$4; }
          | RESIDUAL VARIANCE LIMIT trait_var rnum { if($4>=0 && !check_variance($5,0)) residual_var_limit[$4]=$5; }
          | LIMIT RESIDUAL VARIANCE rnum { if(!check_variance($4,0)) residual_var_limit[0]=$4; }
          | LIMIT RESIDUAL VARIANCE trait_var rnum { if($4>=0 && !check_variance($5,0)) residual_var_limit[$4]=$5; }
          ;

addvarcommand: GENETIC VARIANCE rnum { if(!check_variance($3,0)) {add_var_set[0]=start_flag; additive_var[0]=$3;} }
          | GENETIC VARIANCE trait_var rnum { if($3>=0 && !check_variance($4,0)) {add_var_set[$3]=start_flag; BB(additive_var,$3,$3)=$4;} }
          ;

limitaddvarcommand: GENETIC VARIANCE LIMIT rnum { if(!check_variance($4,0)) additive_var_limit[0]=$4; }
          | GENETIC VARIANCE LIMIT trait_var rnum { if($4>=0 && !check_variance($5,0)) additive_var_limit[$4]=$5; }
          | LIMIT GENETIC VARIANCE rnum { if(!check_variance($4,0)) additive_var_limit[0]=$4; }
          | LIMIT GENETIC VARIANCE trait_var rnum { if($4>=0 && !check_variance($5,0)) additive_var_limit[$4]=$5; }
          ;

meancommand: MEAN rnum { if(n_models>1) yyerror("Model must be specified when multiple models are present"); 
	                      else {grand_mean_set[0]=start_flag; grand_mean[0]=$2; } }
          | MEAN trait_var rnum { if($2>=0) {grand_mean_set[$2]=start_flag; grand_mean[$2]=$3;} }
          ;

positioncommand: POSITION lkvar rnum { set_position($2,$3,$3); }
        | POSITION lkvar rnum ',' rnum { sex_map=1; set_position($2,$3,$5); }
        ;

frequencycommand: FREQUENCY {c_flag=0;} lkmarker freqlist
		  | FREQUENCY {c_flag=1;} COUNTS lkmarker freqlist
        | COUNTS {c_flag=1;} lkmarker freqlist
        ;

trait_var: lkvar {$$=find_trait($1);} ;

lkvar: STRING { $$=find_var($1,0,0); free($1);}
       | STRING '(' INTEGER ')' { $$=find_var($1,$3,1); free($1);}
       ;

lkvarlist: lkvar { if($1) {set_output($1); free($1);} }
       | lkvarlist ',' lkvar { if($3) {set_output($3); free($3);} }
       ;

lkmarker: lkvar { freq_marker=check_marker($1); } ;

groupcommand: GROUP ORDER {group_ptr=0;} grouplist {if(group_var && group_ptr<group_var->n_levels) print_scan_err("Line %d: Too few groups in order statement\n",lineno1);}
       ;

group: INTEGER { $$=find_group(yytext,$1,1); }
       | REAL { $$=find_group(yytext,0,0); }
       | STRING { $$=find_group($1,0,0); free($1);}
       ;

grouplist: group {set_group_order($1);}
       | grouplist ',' group {set_group_order($3);}
       ;

allele: INTEGER { $$=find_allele(yytext,freq_marker,$1,1); }
       | REAL { $$=find_allele(yytext,freq_marker,0,0); }
       | STRING { $$=find_allele($1,freq_marker,0,0); free($1); }
       ;

freqlist: allele ',' {group_counter=0; freq_allele=$1;} freqlist1 {if(freq_marker && group_var && group_counter<group_var->n_levels) print_scan_err("Line %d: Too few frequencies specified\n",lineno);}
        | freqlist allele ',' {group_counter=0; freq_allele=$2;} freqlist1 {if(freq_marker && group_var && group_counter<group_var->n_levels) print_scan_err("Line %d: Too few frequencies specified\n",lineno);}
        ;

freqlist1: rnum {set_freq(freq_marker,$1,freq_allele);}
        | '*' {group_counter++;}
        | freqlist1 ',' rnum {set_freq(freq_marker,$3,freq_allele);}
        | freqlist1 ',' '*' {group_counter++;}
        ;

ibdlist: rnum { $$=add_ibd_list($1,0); }
        | ibdlist ',' rnum { $$=add_ibd_list($3,$1); }
        ;

rnum: REAL
    | INTEGER { $$=(double)$1; }
    ;

%%

static int find_trait(struct lk_variable *lkv)
{
	int i,type,mod;
	struct Variable *var;
	
	if(!n_models || (lkv->type!=LK_TYPE_IDVAR && lkv->type!=LK_TYPE_NONIDVAR)) {
		yyerror("Not a trait variable");
		return -1;
	}
	for(mod=0;mod<n_models;mod++) {
		type=models[mod].var.type;
		i=models[mod].var.var_index;
		var=(type&ST_CONSTANT)?id_variable+i:nonid_variable+i;
		if(var==lkv->var.var) break;
	}
	if(mod==n_models) {
		yyerror("Not a trait variable");
		mod= -1;
	}
	return mod;
}

static void set_analyze(char *p)
{
	int i;
	char *com[]={"AFFECTED","NULL","IBD",0};
		
	if(p) {
		i=0;
		while(com[i]) {
			if(!strcasecmp(com[i],p)) break;
			i++;
		}
		if(com[i]) analysis|=(1<<i);
		else yyerror("Invalid parameter to analyze statement");
	}
}

static void set_ibd_mode(char *p)
{
	int i;
	char *com[]={"LOKI","MERLIN","SOLAR","SINGLEPOINT","SINGLE","SINGLE_POINT",0};
		
	if(p) {
		i=0;
		while(com[i]) {
			if(!strcasecmp(com[i],p)) break;
			i++;
		}
		if(com[i]) {
			if(i<3) ibd_mode=i;
			else ibd_mode |=4;
		} else yyerror("Unknown ibd mode");
	}
}

static void set_group_order( int gp)
{
	int i;
	
	if(!group_var || gp<0) return;
	for(i=0;i<group_ptr;i++) if(group_order[i]==gp)	{
		yyerror("Group repeated in order statement");
		return;
	}
	if(group_ptr>=group_var->n_levels) yyerror("Too many groups - internal error?");
	else group_order[group_ptr++]=gp;
}

#ifdef FUNC_NAME
#undef FUNC_NAME
#endif
#define FUNC_NAME "set_output_gen"
static void set_output_gen(char *file,char *link)
{
	int i;
	struct output_gen *p;

	if(link)	{
		for(i=0;i<n_links;i++) if(!strcasecmp(link,linkage[i].name)) break;
		if(i==n_links) return;
		i++;
	} else i=0;
	p=Output_Gen;
	if(!(Output_Gen=malloc(sizeof(struct output_gen)))) ABT_FUNC(MMsg);
	Output_Gen->next=p;
	Output_Gen->file=file;
	Output_Gen->link_group=i;
}

#ifdef FUNC_NAME
#undef FUNC_NAME
#endif
#define FUNC_NAME "add_ibd_list"
static struct IBD_List *add_ibd_list(double x,struct IBD_List *p)
{
	if(!p) {
		if(!(p=malloc(sizeof(struct IBD_List)))) ABT_FUNC(MMsg);
		p->idx=0;
		p->size=32;
		if(!(p->pos=malloc(sizeof(double)*p->size))) ABT_FUNC(MMsg);
	}
	if(p->size==p->idx) {
		p->size*=2;
		if(!(p->pos=realloc(p->pos,sizeof(double)*p->size))) ABT_FUNC(MMsg);
	}
	p->pos[p->idx++]=x;
	return p;
}

static int check_link_name(char *name,int flag)
{
	int i=0;
	
	if(name)	{
		for(i=0;i<n_links;i++) if(!strcasecmp(name,linkage[i].name)) break;
		if(i==n_links) {
			(void)fprintf(stderr,"Warning: linkage group %s not found; IBD positions ignored\n",name);
			i= -1;
		}
	} else {
		if(n_links>1 && !flag) {
			i= -1;
			(void)fprintf(stderr,"Warning: linkage group not specified when number of linkage groups >1; IBD positions ignored\n");
		}
	}
	return i;
}

static void check_previous_list(int i)
{
	if(linkage[i].ibd_est_type) {
		(void)fprintf(stderr,"Warning: overwriting previous IBD settings for linkage group %s\n",linkage[i].name);
		if(linkage[i].ibd_list) {
			free(linkage[i].ibd_list->pos);
			free(linkage[i].ibd_list);
		}
		linkage[i].ibd_list=0;
		linkage[i].ibd_est_type=0;
	}
}

static void set_ibd_list(char *name,struct IBD_List *p,int type)
{
	int i=0,k;
	
	if(type==IBD_EST_GRID) {
		i=-1;
		if(p->idx<3) (void)fprintf(stderr,"Warning: too few parameters (%d) for IBD Grid (3 required); IBD request ignored\n",p->idx);
		else if(p->idx>3) (void)fprintf(stderr,"Warning: too many parameters (%d) for IBD Grid (3 required); IBD request ignored\n",p->idx);
		else if(fabs(p->pos[2])<IBD_MIN_GRID_STEP) (void)fprintf(stderr,"Warning: step size (%g) for IBD Grid < IBD_MIN_GRID_STEP (%g) in loki_ibd.h ; IBD request ignored\n",p->pos[2],IBD_MIN_GRID_STEP);
		else {
			k=1+(int)(.5+(p->pos[1]-p->pos[0])/p->pos[2]);
			if(k>IBD_MAX_GRID) (void)fprintf(stderr,"Warning: grid evaluations requested (%d) for IBD Grid > IBD_MAX_GRID (%d) in loki_ibd.h ; IBD request ignored\n",k,IBD_MAX_GRID);
			else i=0;
		}
	}
	if(!i) i=check_link_name(name,0);
	if(i<0) {
		free(p->pos);
		free(p);
	} else {
		check_previous_list(i);
		linkage[i].ibd_list=p;
		linkage[i].ibd_est_type=type;
	}
}

static void set_ibd_markers(char *name) 
{
	int i;
	
	i=check_link_name(name,1);
	if(i>=0) {
		for(;i<n_links;i++) {
			check_previous_list(i);
			linkage[i].ibd_est_type=IBD_EST_MARKERS;
			if(name) break;
		}
	}
}

static void set_tloci(int a,int b)
{
	if(a<0) {
		if(a== -1) min_tloci=max_tloci=b;
		else start_tloci=b;
	} else if(a<b) {
		min_tloci=a;
		max_tloci=b;
	} else {
		min_tloci=b;
		max_tloci=a;
	}
}

static struct Marker *check_marker(struct lk_variable *lkvar)
{
	struct Marker *mk=0;
	
	if(!lkvar) return 0;
	if(lkvar->type!=LK_TYPE_MARKER)
		yyerror("Attempting to set frequency of a non-marker");
	else mk=lkvar->var.marker;
	free(lkvar);
	return mk;
}

static void set_output(struct lk_variable *lkvar)
{
	int i,j,type,mod;
	struct Variable *var;
	
	if(!lkvar) return;
	for(mod=0;mod<n_models;mod++) {
		for(i=0;i<models[mod].n_terms;i++) {
			type=models[mod].term[i].vars[0].type;
			j=models[mod].term[i].vars[0].var_index;
			if(type&ST_MARKER) {
				if(lkvar->type==LK_TYPE_MARKER && lkvar->var.marker==marker+j) {
					models[mod].term[i].out_flag=1;
				}
			} else if(type&(ST_TRAITLOCUS|ST_ID|ST_SIRE|ST_DAM)) continue;
			else {
				if(type&ST_CONSTANT) var=id_variable+j;
				else var=nonid_variable+j;
				if((lkvar->type==LK_TYPE_IDVAR || lkvar->type==LK_TYPE_NONIDVAR) && lkvar->var.var==var) {
					models[mod].term[i].out_flag=1;
				}
			}
		}
	}
}

static void set_map_range( char *name,double r1,double r2, int flag)
{
	int i;
	double t;
	static char *sexstr[2]={"female","male"};
	
	if(flag!= -1) sex_map=1;
	
	if(name)	{
		for(i=0;i<n_links;i++) if(!strcasecmp(name,linkage[i].name)) {
			if(r2<r1) {
				t=r2;
				r2=r1;
				r1=t;
			}
			if(flag== -1) {
				linkage[i].r1[0]=linkage[i].r1[1]=r1;
				linkage[i].r2[0]=linkage[i].r2[1]=r2;
				linkage[i].range_set[0]=linkage[i].range_set[1]=1;
				(void)printf("Map range for linkage group '%s' set to %g-%gcM\n",name,r1,r2);
			} else {
				linkage[i].r1[flag]=r1;
				linkage[i].r2[flag]=r2;
				linkage[i].range_set[flag]=1;
				(void)printf("Map range (%s) for linkage group '%s' set to %g-%gcM\n",sexstr[flag],name,r1,r2);
			}
			break;
		}
	} else {
		if(flag<0) {
			total_maplength[X_PAT]=r1;
			total_maplength[X_MAT]=r2;
			(void)printf("Total (genome) map length set to (%g,%g)cM\n",r1,r2);
		} else {
			total_maplength[flag]=r1;
			(void)printf("Total (genome) %s map length set to %gcM\n",sexstr[flag],r1);
		}
	}
}

static int find_group( char *p,int gp, int flag)
{
	int i,j;
	char *s;
	
	if(!group_var) return -1;
	j=group_var->n_levels;
	
	if(group_var->rec_flag==ST_STRING) {
		for(i=0;i<j;i++) if(!(strcasecmp(p,group_var->recode[i].string))) return i;
	} else {
		if(!flag) {
			gp=strtol(p,&s,10);
			if(!(*s)) for(i=0;i<j;i++) if(gp==group_var->recode[i].value) return i;
		} else for(i=0;i<j;i++) if(gp==group_var->recode[i].value) return i;
	}
	yyerror("Group not found\n");
	return -1;
}

static int find_allele( char *p, struct Marker *mk,int all, int flag)
{
	int i,j;
	char *s;
	
	if(!mk) return -1;
	j=extra_allele_flag?mk->locus.n_alleles:mk->locus.n_alleles-1;
	
	if(mk->rec_flag==ST_STRING) {
		for(i=0;i<j;i++) if(!(strcmp(p,mk->recode[i].string))) return i;
	} else {
		if(!flag) {
			all=strtol(p,&s,10);
			if(!(*s)) for(i=0;i<j;i++) if(all==mk->recode[i].value) return i;
		} else for(i=0;i<j;i++) if(all==mk->recode[i].value) return i;
	}
	return -1;
}

#ifdef FUNC_NAME
#undef FUNC_NAME
#endif
#define FUNC_NAME "find_var"
static struct lk_variable *find_var( char *p, int idx, int flag)
{
	int i,j=0;
	struct lk_variable *lkv=0;
	
	if(flag) if(idx<1) {
		yyerror("Illegal array index");
		return 0;
	}
	for(i=0;i<n_markers;i++) if(!strcasecmp(marker[i].name,p)) {
		if(flag && idx!=marker[i].index) continue;
		j=LK_TYPE_MARKER;
		break;
	}
	if(!j) for(i=0;i<n_id_records;i++) if(!strcasecmp(id_variable[i].name,p)) {
		if(flag && idx!=id_variable[i].index) continue;
		j=LK_TYPE_IDVAR;
		break;
	}
	if(!j) for(i=0;i<n_nonid_records;i++) if(!strcasecmp(nonid_variable[i].name,p)) {
		if(flag && idx!=nonid_variable[i].index) continue;
		j=LK_TYPE_NONIDVAR;
		break;
	}
	if(!j && !flag) for(i=0;i<n_links;i++) if(!strcasecmp(linkage[i].name,p)) {
		j=LK_TYPE_LINK;
		break;
	}
	if(j) {
		if(!(lkv=malloc(sizeof(struct lk_variable)))) ABT_FUNC(MMsg);
		lkv->type=j;
		switch(j) {
		 case LK_TYPE_MARKER:
			lkv->var.marker=marker+i;
			break;
		 case LK_TYPE_LINK:
			lkv->var.link=linkage+i;
			break;
		 case LK_TYPE_IDVAR:
			lkv->var.var=id_variable+i;
			break;
		 case LK_TYPE_NONIDVAR:
			lkv->var.var=nonid_variable+i;
			break;
		}
	}
	return lkv;
}

static void set_freq(struct Marker *mk, double freq,int allele)
{
	static int fg,fg1;
	int i=0;
	
	group_counter++;
	if(mk) {
		if(group_var) {
			if(!group_ptr)	{
				if(!fg) yyerror("Genetic group order not set");
				fg=1;
				return;
			}
			if(group_counter>group_var->n_levels) {
				if(!fg1) yyerror("Too many frequencies specified (only 1 per genetic group)");
				fg1=1;
				return;
			}
			i=group_order[group_counter-1];
			if(i<0) return;
		}
		if(freq<0.0) {
			yyerror("Invalid (negative) frequency");
			return;
		}
		if(allele>=0 && freq==0.0)	{
			yyerror("Can not set frequency of observed allele to zero\n");
			return;
		}
		if(allele>=0) {
			mk->locus.freq[i][allele]=freq;
		} else {
			if(extra_allele_flag) {
				yyerror("Cannot set frequency of unknown allele\n");
				return;
			}
			allele=mk->locus.n_alleles-1;
			mk->locus.freq[i][allele]+=freq;
		}
		mk->freq_set[i][allele]=start_flag;
		mk->count_flag[i]=c_flag;
	}
	fg1=0;
}

static void set_position(struct lk_variable *lkvar, double pos1, double pos2)
{
	if(lkvar) {
		if(lkvar->type!=LK_TYPE_MARKER) {
			yyerror("Attempting to set position of a non-marker");
			return;
		}
		lkvar->var.marker->locus.pos[X_PAT]=pos1;
		lkvar->var.marker->locus.pos[X_MAT]=pos2;
		lkvar->var.marker->pos_set=start_flag;
		free(lkvar);
	}
}

static int check_variance(const double v,const int fg)
{
	if(v<=0.0) {
		yyerror("Variance must be positive");
		return 1;
	}
	if(n_models>1 && !fg) {
		yyerror("Must specify which model when multiple models are present");
		return 1;
	}
	return 0;
}

void print_scan_err(char *fmt, ...)
{
	va_list args;
	
	va_start(args,fmt);
	(void)vfprintf(stderr,fmt,args);
	va_end(args);
	if((++scan_error_n)>=max_scan_errors) abt(__FILE__,__LINE__,"Too many errors - aborting\n");
}

void print_scan_warn(char *fmt, ...)
{
	va_list args;
	
	if(scan_warn_n<max_scan_warnings)
	{
		va_start(args,fmt);
		(void)vfprintf(stderr,fmt,args);
		va_end(args);
	}
	scan_warn_n++;
}

void yyerror(char *s)
{
     int i;
	
	print_scan_err("Line %d: %s\n%s\n",lineno,s,linebuf);
	if(scan_error_n<=max_scan_errors)
	{
		for(i=1;i<tokenpos;i++) (void)putc('-',stderr);
		(void)fputs("^\n",stderr);
	}
}

int yywrap(void)
{
	return 1;
}

#ifdef FUNC_NAME
#undef FUNC_NAME
#endif
#define FUNC_NAME "ReadParam"
int ReadParam(FILE *fptr,char *cname,int *flag)
{
	int i,j;
	void yy_cleanup(void);
	
	fname_list[0]=cname;
	list_ptr=0;
	ran_flag=flag;
	for(i=0;i<NUM_SYSTEM_VAR;i++) syst_var[i].flag=0;
	for(i=0;i<n_id_records;i++) if(id_variable[i].type&ST_GROUP) {
		group_var=id_variable+i;
		j=group_var->n_levels;
		if(!(group_order=malloc(sizeof(int)*j))) ABT_FUNC(MMsg);
		break;
	}
	yyin=fptr;
	if((i=yyparse())) {
	  (void)fprintf(stderr,"Error: yyparse returned error %d\n",i);
	  scan_error_n++;
	}
	yy_cleanup();
	if(group_order) free(group_order);
	if(start_tloci<0) start_tloci=min_tloci;
	else if(start_tloci<min_tloci || start_tloci>max_tloci) {
		(void)fprintf(stderr,"ReadParam(): Starting no. trait loci (%d) is outside set range (%d-%d)\n",start_tloci,min_tloci,max_tloci);
		scan_error_n++;
	}
	if(n_models>1 && output_type<2) {
		(void)fprintf(stderr,"ReadParam(): Ouput type %d not supported with multilpe trait loci\n",output_type);
		scan_error_n++;
	}
	if(!syst_var[SYST_IBD_OUTPUT].flag) {
		j=ibd_mode;
		if(compress_ibd) j|=COMPRESS_IBD;
		if(j) {
			syst_var[SYST_IBD_OUTPUT].flag=ST_INTEGER;
			syst_var[SYST_IBD_OUTPUT].data.value=j;
		}
	}
	if(scan_error_n) return 1;
	return 0;
}
