#include "head.h"
#include "y.tab.h"


static void generateSimpleElementaryDaemonCall(S_cTree  *t);

/* ******************************************************************* */

void putNodeToDepNodes(S_cTree *tr, S_compilerVar *v) {
	register S_cTreeList 	*ll, **place;
//&	if (IsConstantNode(tr)) return;
	if (IsConstant(v)) return;
	SORTED_LIST_PLACE(place, S_cTreeList, tr, t, &v->depNodes);
	if (*place == NULL || (*place)->t != tr) {
		// new item insert it
		ALLOC(ll, S_cTreeList);
		FILL_cTreeList(ll, tr, NULL);
		LIST_CONS(ll, (*place));
//&fprintf(dumpOut,"inserting to var %d the node ",v->order);cTreeDump(tr);fprintf(dumpOut,"\n"); fflush(dumpOut);
	}
}

void affectSingleVarOrder(S_compilerVar *v) {
	if (v->order == ORDER_NOT_SET) {
		v->order = s_cVarOrder++;
		if (v->order > MAX_VARS) {
			sprintf(tmpBuff, "more than %d variables, sorry", MAX_VARS);
 			fatalError(ERR_ST, tmpBuff);
		}
	}
}

static void affectVarOrder( S_cTree *tt ) {
	S_compilerVar *v;
	S_compilerType *t;
	int i,dim;
	v = tt->s.var;
	t = tt->s.type;
	dim = getTypeDimension(t);
	for(i=0; i<dim; i++) {
		affectSingleVarOrder(&v[i]);
	}
	if (s_opt.listConstraints) {
		fprintf(dumpOut,"var[%d] (forOrder %d) == ", v->order, v->forOrder);
		if (IsConstant(v)) fprintf(dumpOut,"%d",v->min);
		else cTreeDump(tt);
		fprintf(dumpOut,"\n");
	}
}

static void putDepencesToArrayElements( S_cTree *at ) {
	int i, dim;
	S_compilerVar *aa;
	if (at->s.b.makeVarTabProcessed==0) {
		at->s.b.makeVarTabProcessed=1;
		dim = getTypeDimension(at->s.type);
		aa = at->s.var;
		for(i=0; i<dim; i++) {
			putNodeToDepNodes(at, &aa[i]);
		}
	}
}



static void makeVarTableInExpr(S_cTree *t) {
	int 			i;
	S_compilerVar	*v;
	S_cTree			*at;
	assert(t!=NULL);
	v = t->s.var;
	assert(v);
	if (t->s.b.makeVarTabProcessed) return;
	t->s.b.makeVarTabProcessed = 1;
//&fprintf(dumpOut,"[makeVarTableInExpr] examining "); cTreeDump(t); fprintf(dumpOut,"\n"); fflush(dumpOut);
	affectVarOrder( t);
	switch (t->fsym) {
	case IDENT:	case CONSTANT:
		break;
	case '[':
		assert(t->arity==2 && t->sub[0] && t->sub[1] && t->sub[0]->sub[0]);
		at = t->sub[0];
		assert(at->fsym==AARRAY);
		putNodeToDepNodes(t, v);
		putNodeToDepNodes(t, at->sub[0]->s.var);
		putNodeToDepNodes(t, t->sub[1]->s.var);
		makeVarTableInExpr(t->sub[1]);
		putDepencesToArrayElements(at);
		break;
	default:
		putNodeToDepNodes(t, v);
		for (i=0; i<t->arity; i++) {
			putNodeToDepNodes(t, t->sub[i]->s.var);
			makeVarTableInExpr(t->sub[i]);
		}
	}
}

void makeVarTable(S_cTreeList *csp) {
	S_cTreeList 	*pp;
	S_cTree			*t;
	for(pp=csp; pp!=NULL; pp=pp->next) {
		t = pp->t;
		makeVarTableInExpr(t);
	}
}

/* ******************************************************************* */

char * propagationFunctionName(int fsym) {
	if (fsym < LAST_TOKEN) {
		assert(s_tokenTab[fsym].funName!=NULL);
		return(s_tokenTab[fsym].funName);
	} else {
		assert(s_strTab.tab[fsym-LAST_TOKEN]);
		return(s_strTab.tab[fsym-LAST_TOKEN]);
	}
}

static char * generateFaktorOfProcVar(int f, char *vname) {
	static char ff[TMP_STRING_SIZE];
	ff[0]=0;
	if (f!=0) {
		assert(vname != NULL);
		if (f==1) sprintf(ff, "%s", vname);
		else if (f== -1) sprintf(ff, "-%s", vname);
		else sprintf(ff, "%d*%s", f, vname);
	}
	return(ff);
}

static char * generateOffsetOfProcVar(int o) {
	static char ff[TMP_STRING_SIZE];
	ff[0]=0;
	if (o!=0) {
		if (o>0) sprintf(ff,"+%d",o);
		else sprintf(ff,"-%d",-o);
	}
	return(ff);
}

static char * generateBaseProcVarNumber(
										int faktor, int offset,
										int divisor,
										char *vn
	) {
	static char 	ff[TMP_STRING_SIZE];
	int 			f,o,d;
	f = faktor;
	o = offset;
	d = divisor;
	if (f<0 && d<0) {f = -f; o = -o; d = -d;}
	if (f==0 && o==0) {
		sprintf(ff,"0");
		return(ff);
	} else if (f%d!=0) {
		sprintf(ff, "(%s%s)/%d", 
				generateFaktorOfProcVar(f,vn),
				generateOffsetOfProcVar(o),
				d);
		return(ff);
	} else if (f%d==0 && o%d!=0) {
		sprintf(ff, "%s%s/%d", 
				generateFaktorOfProcVar(f/d,vn),
				generateOffsetOfProcVar(o),
				d);
		return(ff);
	} else if (f%d==0 && o%d==0) {
		sprintf(ff, "%s%s", 
				generateFaktorOfProcVar(f/d,vn),
				generateOffsetOfProcVar(o/d)
				);
		return(ff);
	} else {
		assert(0);
		return(NULL);
	}
}

static int generateGenericsIsConstant(S_genericArg *sub, int x2mx1, int *val) {
	if (sub->fx2mfx1 != 0) return(0);
	// yes it is a constant, compute value
	*val = sub->x2fx1mx1fx2 / x2mx1;
	return(1);
}


static void generateGenericBaseProcVarNumber(S_genericArg *factp, 
											 S_genericArg *offp,
											 int x2mx1, int x2mx12,
											 char *iname, char *vname
	) {
	char			*ss;
	int				valf, valo;
	if (	generateGenericsIsConstant(factp, x2mx12, &valf)
			&& generateGenericsIsConstant(offp, x2mx12, &valo)) {
		ss = generateBaseProcVarNumber(valf, valo, x2mx1, iname);
		fprintf(ccOut,"%s",ss);
	} else if (generateGenericsIsConstant(factp, x2mx12, &valf) && valf==0) {
		ss = generateBaseProcVarNumber(offp->fx2mfx1, offp->x2fx1mx1fx2, 
								   x2mx12*x2mx1, vname);
		fprintf(ccOut,"%s ", ss);
	} else {
		if (x2mx1 == -1) {
			fprintf(ccOut,"-"); 
			x2mx1 = -x2mx1;
		}
		ss = generateBaseProcVarNumber(factp->fx2mfx1, factp->x2fx1mx1fx2, 
								   x2mx12, vname);
		fprintf(ccOut,"((%s)*%s", ss, iname);
		ss = generateBaseProcVarNumber(offp->fx2mfx1, offp->x2fx1mx1fx2, 
								   x2mx12, vname);
		fprintf(ccOut,"+%s)", ss);
		if (x2mx1 != 1) fprintf(ccOut,"/%d ", x2mx1);
	}
}


static void generateGenericGenericElementaryDaemonCall(
	int fsym,
	S_genericArg *sub, int x2mx1, int x2mx12, 
	char *iname, char *jname, int arity
	) {
	int 			i;
	switch (fsym) {
	case IDENT:	case CONSTANT:
		break;
	case AARRAY:
		assert(sub[2].fx2mfx1 == 0);
		assert(sub[2].x2fx1mx1fx2 == 0);
		assert(sub[3].fx2mfx1 == 0);
		fprintf(ccOut, "/*ARRAY*/ vh%d(%s)",sub[3].x2fx1mx1fx2/(x2mx1*x2mx12),
				iname);
		break;
	default:
		fprintf(ccOut,"%s( ", propagationFunctionName(fsym));
		generateGenericBaseProcVarNumber(&sub[0], &sub[1], x2mx1, 
										 x2mx12,iname,jname);
		for (i=0; i<arity; i++) {
			fprintf(ccOut,", "); 
			generateGenericBaseProcVarNumber(&sub[i*2+2], &sub[i*2+3], x2mx1,
											 x2mx12,iname,jname);
			fflush(ccOut);
		}
		fprintf(ccOut,")");
	}
	fflush(stdout);
}

void generateGenericElementaryDaemonCall(S_procedure *t,int x2mx1,char *vname){
	S_genericArg	args[MAX_ARITY*2+2];
	int 			i, fs, arity;
	assert(t!=NULL);
	arity = t->arity;
	fs = t->fsym;
	for(i=0;i<arity*2+2; i++) {
		FILL_genericArg(&args[i], 0, 0);
	}
	args[1] = t->res;
	for(i=0; i<arity; i++) args[i*2+3] = t->sub[i];
	generateGenericGenericElementaryDaemonCall(fs, args, 1, x2mx1, 
											   vname, vname, arity);
}

void generateSimpleElementaryDaemonCall(S_cTree *t) {
	S_genericArg	args[MAX_ARITY*2+2];
	int 			i, fs, arity;
	assert(t!=NULL);
	arity = t->arity;
	fs = t->fsym;
	for(i=0;i<arity*2+2; i++) {
		FILL_genericArg(&args[i], 0, 0);
	}
	FILL_genericArg(&args[1], 0, t->s.var->order);
	for(i=0; i<arity; i++) {
		FILL_genericArg(&args[i*2+3], 0, t->sub[i]->s.var->order);
	}
	generateGenericGenericElementaryDaemonCall(fs, args, 1, 1, 
											   "i", NULL, arity);
}


static int operatorCallOrderingLess(S_cTreeList *l1, S_cTreeList *l2) {
	register int i,a,o1,o2;
	register S_cTree *t1, *t2;
	register S_cTree **s1, **s2;
	t1 = l1->t; t2 = l2->t;
	assert(t1 && t2);
	assert(t1->fsym!=IDENT);
	assert(t1->fsym!=CONSTANT);
	assert(t2->fsym!=IDENT);
	assert(t2->fsym!=CONSTANT);
	if (t1->fsym < t2->fsym) return(1);
	if (t1->fsym > t2->fsym) return(0);
	a = t1->arity;
	assert(a == t2->arity);
	s1 = t1->sub; s2 = t2->sub;
	for(i=0; i<a; i++)	{
		o1 = s1[i]->s.var->order;
		o2 = s2[i]->s.var->order;
		if (o1 < o2) return(1);
		if (o1 > o2) return(0);
	}
	return(0);
}

static void genericParamsForGenericDaemonSet(S_genericArg *sub, 
											 int x1, int x2, 
											 int *fx1, int *fx2, 
											 int maxx
	) {
	int i;
	for(i=0; i<maxx; i++) {
/*&
  fprintf(dumpOut,"creating pars for :\t%d\t-> %d\n", x1, fx1[i]);
  fprintf(dumpOut,"                  :\t%d\t-> %d\n", x2, fx2[i]);
  fflush(dumpOut);
&*/
		FILL_genericArg(&sub[i], fx2[i]-fx1[i], x2*fx1[i]-x1*fx2[i]);
	}
}



static void genericParamsForSingleDaemonSet(S_procedure *pp, 
											int x1, int x2 ,
											S_cTree *t1, S_cTree *t2
	) {
	S_genericArg 	*sub;
	S_cTree 		**s1, **s2;
	int 			i, fx1, fx2;

	assert(t1->fsym == t2->fsym);
	sub = pp->sub;
	s1 = t1->sub;
	s2 = t2->sub;
	for(i=0; i<t1->arity; i++) {
		fx1 = s1[i]->s.var->order;
		fx2 = s2[i]->s.var->order;
/*&
  fprintf(dumpOut,"creating pars for :\t%d\t-> %d\n", x1, fx1);
  fprintf(dumpOut,"                  :\t%d\t-> %d\n", x2, fx2);
  fflush(dumpOut);
  &*/
		FILL_genericArg(&sub[i], fx2-fx1, x2*fx1-x1*fx2);
	}
	fx1 = t1->s.var->order;
	fx2 = t2->s.var->order;
	FILLF_procedure(pp, t1->fsym, t1->arity, fx2-fx1, x2*fx1-x1*fx2,
					sub, NULL);
}



static void createProcedureParameters(		S_compilerVar *v,
											S_procedureTabItem *p
	) {
	S_procedure 	*pp, **ppp;
	S_cTreeList 	*l1, *l2;
	S_cTree 		*t1, *t2;
	S_genericArg	*sub;
	int				x1,x2;
	x1 = v->order;
	x2 = p->v->order;
	p->x2mx1 = x2 - x1;
	if (x1==x2) fprintf(stderr,"x1==x2==%d\n",x1);
	assert(p->x2mx1 != 0);
	ppp = &p->p;
	for(l1=v->depNodes,l2=p->v->depNodes; l1!=NULL; l1=l1->next,l2=l2->next) {
		assert(l1 && l2);
		t1=l1->t; t2=l2->t;
		ALLOC(pp, S_procedure);
		ALLOCC(sub, t1->arity, S_genericArg);
		pp->sub = sub;
		genericParamsForSingleDaemonSet( pp, x1, x2, t1, t2);
		*ppp = pp;
		ppp = &pp->next;
	}
	assert(l2==NULL);
}

static int computeVarNum(S_genericArg *a, int x, int x2mx1) {
	int res;
	assert(x2mx1!=0);
	res = (x * a->fx2mfx1 + a->x2fx1mx1fx2) / x2mx1;
	return(res);
}

static int genericGenericParametersFit(S_genericArg *sub, int *fxx, int x, 
							int x2mx1, int maxx) {
	int i, fx;
	for(i=0; i<maxx; i++) {
		fx = computeVarNum(&sub[i], x, x2mx1);
		if (fx != fxx[i]) return(0);
	}
	return(1);
}

static int genericParametersFit(S_procedure *pp, 
								  S_cTree *t, 
								  int x, 
								  int x2mx1
	) {
	S_cTree **s;
	S_genericArg *sub;
	int i, fx;
	s = t->sub;
	sub = pp->sub;
	fx = computeVarNum(&pp->res, x, x2mx1);
	if (fx != t->s.var->order) return(0);
	for(i=0; i<t->arity; i++) {
		fx = computeVarNum(&sub[i], x, x2mx1);
		if (fx != s[i]->s.var->order) return(0);
	}
	return(1);
}


static int canBeGeneratedByThisProcedure(	S_compilerVar *v,
											S_procedureTabItem *p
	) {
	S_procedure 	*pp;
	S_cTreeList 	*l;
	S_cTree 		*t;
	int				x,x2mx1,r;
	x = v->order;
	x2mx1 = p->x2mx1;
	for(l=v->depNodes,pp=p->p; l!=NULL; l=l->next,pp=pp->next) {
		assert(l && pp);
		t=l->t;
		assert(t->fsym == pp->fsym);
		r = genericParametersFit( pp, t, x, x2mx1);
		if (r==0) return(0);
	}
	assert(pp==NULL);
//&fprintf(dumpOut,"the same procedure will be called\n");fflush(dumpOut);
	return(1);
}


static void procParamsDump(S_procedureTabItem  *pp) {
	S_cTreeList		*l;
	S_procedure  	*p;
	int				x2mx1;
	x2mx1 = pp->x2mx1;
	printf("\nvarProcedureForVar %d {\n", pp->v->order);
	for(p=pp->p,l=pp->v->depNodes; p!=NULL; p=p->next,l=l->next) {
		generateSimpleElementaryDaemonCall(l->t);
		printf("\t\t==\t");
		generateGenericElementaryDaemonCall(p, x2mx1,"i");
		printf("\n");
	}
	printf("}\n");
}

static S_procedureTabItem *yetGeneratedVarHandler(S_compilerVar *v) {
	S_procedureTabItem 	dd,*memb,*ncandid;
	int					ii;
	FILL_procedureTabItem(&dd, v, NULL, 0, NULL, NULL);
	if (! procTabIsMember(&s_procTab, &dd, &ii, &memb)) return(NULL);
//&fprintf(dumpOut,"this sequence is yet in the table as #%d\n",memb->v->order); fflush(dumpOut);
	ncandid = NULL;
	do {
		if (memb->p != NULL) {
//&fprintf(dumpOut,"checking pars for #%d\n",memb->v->order);fflush(dumpOut);
			if (canBeGeneratedByThisProcedure(v, memb)) return(memb);
		} else {
			ncandid = memb;
		}
	} while (procTabNextMember(&dd, &memb));
	if (ncandid == NULL) return(NULL);
//&fprintf(dumpOut,"creating proc parameters for #%d\n",ncandid->v->order);fflush(dumpOut);
	createProcedureParameters(v, ncandid);
//&procParamsDump(ncandid);
	return(ncandid);
}

static void depNodesDump(S_compilerVar *v) {
	S_cTreeList  *l;
	printf("\nvarHanlerForVar %d{\n",v->order);
	for(l=v->depNodes; l!=NULL; l=l->next) {
		generateSimpleElementaryDaemonCall(l->t);
		printf("\t\t==\t");
		cTreeDump(l->t);
		printf("\n");
	}
	printf("}\n");
}	

static void createVarHandler(S_compilerVar *v) {
	S_procedureTabItem 	*p;
	S_compilerVarList	*vl;
	int					ii;
	if (IsConstant(v)) return;
	if (v->b.varHandleGenerated) return;
	v->b.varHandleGenerated = 1;
	LIST_SORT(S_cTreeList, v->depNodes, operatorCallOrderingLess);
//&depNodesDump(v);
	p = yetGeneratedVarHandler(v);
	if (p==NULL) {
//&fprintf(dumpOut,"new procedure will be created\n");
		ALLOC(p, S_procedureTabItem);
		FILL_procedureTabItem(p, v, NULL, 0, NULL, NULL);
		procTabPut(&s_procTab, p, &ii);
	}
	ALLOC(vl, S_compilerVarList);
	FILL_compilerVarList(vl, v, p->handledVars);
	p->handledVars = vl;
}

static void createVarHandlersForExprs(S_cTree *t) {
	int 			i,dim;
	S_compilerVar	*v,*aa;
	assert(t!=NULL);
	v = t->s.var;
	assert(v);
	switch (t->fsym) {
	case CONSTANT:
		break;
	case IDENT:
		createVarHandler(t->s.var);
		break;
	case AARRAY:
		dim = getTypeDimension(t->s.type);
//&fprintf(ccOut, "dim == %d\n", dim);
		aa = t->s.var;
		for(i=0; i<dim; i++) {
			createVarHandler(&aa[i]);
		}
		assert(t->sub[0]);
		createVarHandler(t->sub[0]->s.var);
		break;
	default:
		createVarHandler(t->s.var);
		for (i=0; i<t->arity; i++) {
			createVarHandlersForExprs(t->sub[i]);
		}
	}
}

void createVarHandlers(S_cTreeList *csp) {
	S_cTreeList 	*pp;
	S_cTree			*t;
	for(pp=csp; pp!=NULL; pp=pp->next) {
		t = pp->t;
		createVarHandlersForExprs(t);
	}
}

/* ********************************************************************* */

static void genericGenericDaemonParamsFill( S_procedure *ll, int *fx ) {
	int i, arity;
	arity = ll->arity;
	fx[0] = ll->res.fx2mfx1;
	fx[1] = ll->res.x2fx1mx1fx2;
	for(i=0; i<arity; i++) {
		fx[i*2+2] = ll->sub[i].fx2mfx1;
		fx[i*2+3] = ll->sub[i].x2fx1mx1fx2;
	}
}

static void generateForLoopForGenericDaemonCall(S_procedure **lll, int x2mx1) {
	S_procedure 	*ll, *ll2, *l;
	int				i,arity,arity2,x2mx12,x1,x2,handledn;
	S_genericArg	args[MAX_ARITY*2+2];
	int				fx[MAX_ARITY*2+2];
	int				fx1[MAX_ARITY*2+2];
	int				fx2[MAX_ARITY*2+2];
	
	ll = *lll;
	assert(ll!=NULL);
	if (ll->next == NULL) {
		fprintf(ccOut,"\t");
		generateGenericElementaryDaemonCall(ll, x2mx1, "i");
		fprintf(ccOut,";\n");
		ll=ll->next;
		goto fini;
	}
	ll2=ll->next; 
	arity = ll->arity; arity2 = arity*2+2;
	/* create parameters */
	x1 = 0; x2 = 1;
	x2mx12 = x2-x1;
	genericGenericDaemonParamsFill( ll, fx1);
	genericGenericDaemonParamsFill( ll2, fx2);
	genericParamsForGenericDaemonSet(args, x1, x2, fx1, fx2, arity2);

	for(l=ll,i=0;l!=NULL;l=l->next, i++) {
		genericGenericDaemonParamsFill(l, fx);
		if (genericGenericParametersFit(args, fx, i, x2mx12, arity2)==0) break;
	}
	handledn = i;
	fprintf(ccOut,"\tfor(j=0; j<%d; j++) {\n\t\t", handledn);
	generateGenericGenericElementaryDaemonCall(ll->fsym, args, x2mx1, x2mx12, 
											   "i", "j", arity);
	fprintf(ccOut,";\n\t}\n");
	ll = l;
 fini:
	*lll = ll;
	return;
}


static void generateForLoopForSingleDaemonCall(S_cTreeList **lll) {
	S_cTreeList 	*ll, *l;
	S_cTree			*tt1, *tt2;
	int				i,arity,x2mx1,x1,x2,handledn;
	S_procedure		pp;
	S_genericArg	args[MAX_ARITY];
	
	pp.sub = args;
	ll = *lll;
	assert(ll!=NULL);
	if (ll->next == NULL) {
		fprintf(ccOut,"\t");
		generateSimpleElementaryDaemonCall(ll->t);
		fprintf(ccOut,";\n");
		ll=ll->next;
		goto fini;
	}
	tt1 = ll->t; tt2=ll->next->t;
	FILL_procedure(&pp, tt1->fsym, tt1->arity, args[0], args, NULL);
	/* create parameters */
	arity = tt1->arity;
	x1 = 0; x2 = 1;
	x2mx1 = x2-x1;
	genericParamsForSingleDaemonSet(&pp, x1, x2, tt1, tt2);
	for(l=ll,i=0;l!=NULL;l=l->next, i++) {
		if (genericParametersFit(&pp,l->t,i,x2mx1)==0) break;
	}
	handledn = i;
	fprintf(ccOut,"\tfor(j=0;j<%d; j++) {\n\t\t", handledn);
	generateGenericElementaryDaemonCall(&pp, x2mx1,"j");
	fprintf(ccOut,";\n\t}\n");
	ll = l;
 fini:
	*lll = ll;
	return;
}

static void generateGenericDaemonsCall(S_procedure **ttt, int x2mx1) {
	int 			fs,l;
	S_procedure 	*tt,*nextt,*firstt,*lastt;
	tt = *ttt;
	fs = tt->fsym;
	l=0; 
	tt = lastt = firstt = *ttt;
	while(tt!=NULL && tt->fsym==fs) {
		l++; lastt=tt; tt=tt->next;
	}
	nextt = tt;
	assert(lastt->next == nextt);
	lastt->next = NULL;
	if (l<=5 || fs==AARRAY/*not optimize this call, (for now)*/) {
		for(tt= firstt; tt!=NULL; tt=tt->next) {
			fprintf(ccOut,"\t");
			generateGenericElementaryDaemonCall(tt, x2mx1,"i");
			fprintf(ccOut,";\n");
		}
	} else {
		tt = firstt;
		while (tt!=NULL) generateForLoopForGenericDaemonCall(&tt, x2mx1);
	}
	lastt->next = nextt;
	*ttt = nextt;
}

static void generateSimpleDaemonsCall(S_cTreeList **ttt) {
	int 			fs,l;
	S_cTreeList 	*tt,*nextt,*firstt,*lastt;
	tt = *ttt;
	assert(tt->t!=NULL);
	fs = tt->t->fsym;
	l=0; 
	tt = lastt = firstt = *ttt;
	while(tt!=NULL && tt->t->fsym==fs) {
		l++; lastt=tt; tt=tt->next;
	}
	nextt = tt;
	assert(lastt->next == nextt);
	lastt->next = NULL;
	if (l<=5 || fs==AARRAY/*not optimize this call, (for now)*/) {
		for(tt= *ttt; tt!=NULL; tt=tt->next) {
			fprintf(ccOut,"\t");
			generateSimpleElementaryDaemonCall(tt->t);
			fprintf(ccOut,";\n");
		}
	} else {
		tt = *ttt;
		while (tt!=NULL) generateForLoopForSingleDaemonCall(&tt);
	}
	lastt->next = nextt;
	*ttt = nextt;
}

/* ********************************************************************* */

static int isLessVarList(S_compilerVarList *e1, S_compilerVarList *e2) {
	return(e1->v->order < e2->v->order);
}

static void generateForLoopForVarList(	S_compilerVarList **ll, int ivar) {
	S_compilerVarList *l;
	int	min,i,f;
	l = *ll;
	assert(l!=NULL);
	if (l->next == NULL) {
		fprintf(ccOut,"\tcs_varHandlerFunTab[%d]=vh%d;\n", 
				l->v->order, ivar);
		l=l->next;
		goto fini;
	}
	min = l->v->order;
	f = l->next->v->order - l->v->order;
	for(i=0; l!=NULL && l->v->order == min+i*f; i++,l=l->next) ;
	assert(f!=0);
	fprintf(ccOut,"\tfor(j=%d;j<%d;j+=%d) cs_varHandlerFunTab[j]=vh%d;\n",
			min, min+i*f, f, ivar);
 fini:
	*ll = l;
	return;
}

static void generateHandledVarsInit(S_procedureTabItem  *p) {
	S_compilerVarList *l;
	fprintf(ccOut,"static void v%di() {\n", p->v->order);
	fprintf(ccOut,"\tint j;\n");
	LIST_SORT(S_compilerVarList, p->handledVars, isLessVarList);
	l=p->handledVars; 
	while (l!=NULL) generateForLoopForVarList(&l, p->v->order);
	fprintf(ccOut, "}\n");
}

static void generateGenericVarHandlerBody(S_procedureTabItem  *pp) {
	S_procedure  	*p;
	int				x2mx1;
	STATISTICS(STS_genericProcs);
	x2mx1 = pp->x2mx1;
	p = pp->p;
	while (p!=NULL) generateGenericDaemonsCall(&p, x2mx1);
}

static void generateSimpleVarHandlerBody(S_procedureTabItem  *pp) {
	S_cTreeList		*l;
	STATISTICS(STS_simpleProcs);
	l = pp->v->depNodes;
	while (l!=NULL) generateSimpleDaemonsCall(&l);
}

static void generateVarHandler(S_procedureTabItem  *p) {
	S_compilerVar *v;
	v = p->v;
	STATISTICS(STS_procedureGenerated);
	fprintf(ccOut,"static void vh%d(int i) {\n\tregister int j;", v->order);
//&	fprintf(ccOut,"/*fo %d*/", v->forOrder);
	fprintf(ccOut,"\n");
	if (p->p != NULL) generateGenericVarHandlerBody(p);
	else generateSimpleVarHandlerBody(p);
	fprintf(ccOut,"}\n");
	if (p->p != NULL) generateHandledVarsInit(p);
	fprintf(ccOut,"\n");
}

static void generateVarHandlerPrototype(S_procedureTabItem  *p) {
	S_compilerVar *v;
	v = p->v;
	fprintf(ccOut,"static void vh%d(int i);\n", v->order);
}

static void generateSetInitVarHandler(S_procedureTabItem  *p) {
	S_compilerVar *v;
	v = p->v;
	if (p->p == NULL) {
		fprintf(ccOut,"\tcs_varHandlerFunTab[%d]=vh%d;\n",
				v->order, v->order);
	} else {
		fprintf(ccOut,"\tv%di();\n",v->order);
	}
}

/* ********************** USER VARIABLE INTERFACE ************************ */

static void generateCFrontDeclarator(S_compilerType *typ,char *name) {
	S_compilerType *aa;
	if (typ->m==TypeInt || (typ->m==TypeArray && typ->next->next==NULL)) {
		fprintf(ccOut,"struct variable *%s", name);
	} else {
		fprintf(ccOut,"struct variable (*%s)", name);
		for(aa=typ; aa->m==TypeArray; aa=aa->next) {
			fprintf(ccOut, "[%d]", aa->dim);
		}
	}
}

static void generateCFrontVarDeclaration(S_symbol *s) {
	assert(s && s->ct && s->ct->s.var);
	generateCFrontDeclarator(s->ct->s.type, s_strTab.tab[s->name]);
	fprintf(ccOut," = (");
	generateCFrontDeclarator(s->ct->s.type, "");
	fprintf(ccOut, ") &cs_varTab[%d]", s->ct->s.var->order);
	fprintf(ccOut,";\n");
}

/* ********************** INITIALISATIONS ************************ */

static void generateInitialisationSymbol(S_symbol *s) {
	S_parseTree 	*l;
	int 			i,len,min,max,dim;
	S_compilerVar	*v;
	l = s->init;
	if (l==NULL) return;
	v = s->ct->s.var;
	dim = getTypeDimension(s->ct->s.type);
	fprintf(ccOut,"\t%d,%d,", v->order, dim);
	assert(l->fsym == ACONS);
	LIST_GEN_LEN(len, S_parseTree, sub[1], l);
	fprintf(ccOut,"%d,", len);
	i=0;
	LIST_GEN_MAP(S_parseTree, sub[1], l, {
		getInitialisationInterval(tmp, &min, &max);
		fprintf(ccOut,"%d,%d,", min, max);
		if (i<dim && v[i].min==min && v[i].max==max) {
			v->b.varInitializerGenerated = 1;
//&fprintf(dumpOut, "final init of v%d[%d]\n", v->order, i); 
		}
		i++;
	});
	fprintf(ccOut,"\n");
}

static void generateConstantInitialisation(S_cTreeList *l) {
	S_compilerVar	*v;
	v = l->t->s.var;
	if (v->order == ORDER_NOT_SET) return;
	if (v->b.varInitializerGenerated) return;
	v->b.varInitializerGenerated = 1;
	if (v->min==MINIMAL_SOLVE_INT && v->max==MAXIMAL_SOLVE_INT) return;
	fprintf(ccOut,"\t%d,1,1,%d,%d,\n", v->order, v->min, v->max);
}

/* ********************** VARIABLE NAMES ************************ */

static void generateVarNamesTable(S_symbol *s) {
	int order,dim;
	order = s->ct->s.var->order;
	dim = getTypeDimension(s->ct->s.type);
	fprintf(ccOut, "\t{\"%s\", %d, %d},\n", s_strTab.tab[s->name], 
			order, order+dim);
}

/* ********************** INTERPRETER ************************ */

static void generateInterpreterVarInfo(S_cTreeList *ll) {
	S_compilerVar	*v;
	S_cTreeList		*l;
	S_cTree			*t;
	int				i,len;
	v = ll->t->s.var;
	if (v->order == ORDER_NOT_SET) return;
	if (v->b.varHandleGenerated) return;
	v->b.varHandleGenerated = 1;
	if (IsConstant(v)) return;
	LIST_LEN(len, S_cTreeList, v->depNodes);
	fprintf(ccOut,"\t/*handler start*/%d,%d, ", v->order, len);
	for(l=v->depNodes; l!=NULL; l=l->next) {
		t = l->t;
		fprintf(ccOut,"\n\t\t(int)&%s,%d, ", propagationFunctionName(t->fsym),
				t->arity+1);
		fprintf(ccOut,"%d,", t->s.var->order);
		for(i=0; i<t->arity; i++) {
			fprintf(ccOut,"%d,", t->sub[i]->s.var->order);
		}
	}
	fprintf(ccOut,"\n");
}

/* ********************** INITIALISATIONS ************************ */

void generateForwardCheckings() {
	if (s_opt.interpreter) {
		fprintf(ccOut,"#include \"daemons.c\"\n\n");
	} else {
/*
		if (s_opt.inlined) {
			fprintf(ccOut,"#include \"inlined.h\"\n\n");
		}
*/
		procTabMap(&s_procTab, generateVarHandlerPrototype);
		fprintf(ccOut,"\n");
		procTabMap(&s_procTab, generateVarHandler);
	}

	fprintf(ccOut,"int cs_numberOfVars = %d;\n", s_cVarOrder);
	fprintf(ccOut,"struct variable cs_varTab[%d];\n", s_cVarOrder);
	fprintf(ccOut,"char s_deltaVar[%d];\n", s_cVarOrder);
	fprintf(ccOut,"char s_rangeEventBitm[%d];\n", s_cVarOrder);
	fprintf(ccOut,"char s_valueEventBitm[%d];\n", s_cVarOrder);
	fprintf(ccOut,"struct trailElem *s_varTrailTab[%d];\n", s_cVarOrder);
	fprintf(ccOut, "\n");

	fprintf(ccOut,"void (*cs_varHandlerFunTab[%d])();\n", s_cVarOrder);
	if (s_opt.interpreter) {
		fprintf(ccOut,"int cs_interpreterPropagationInfo[] = {0,\n");
		cTreeTabMap(&s_cTreeTab, generateInterpreterVarInfo);
		fprintf(ccOut, "\t-1,\n};\n");
		fprintf(ccOut,"int cs_interpreterPropVarInfo[%d];\n", s_cVarOrder);
		fprintf(ccOut,"void initializeHandlerFunctions() {}\n");
		fprintf(ccOut,"int cs_interpreter = 1;\n");
	} else {
		fprintf(ccOut,"int cs_interpreterPropagationInfo[1];\n");
		fprintf(ccOut,"int cs_interpreterPropVarInfo[1];\n");
		fprintf(ccOut,"int cs_interpreter = 0;\n");
		fprintf(ccOut,"void initializeHandlerFunctions() {\n");
		procTabMap(&s_procTab, generateSetInitVarHandler);
		fprintf(ccOut,"}\n\n");
	}

	fprintf(ccOut, "struct cs_variableNameStr cs_variableNames[]={\n");
	symbolTabMap(&s_symbolTab, generateVarNamesTable);
	fprintf(ccOut, "\t{NULL,-1,-1}\n};\n\n");

	fprintf(ccOut, "int cs_initialisationInfo[]={\n");
	symbolTabMap(&s_symbolTab, generateInitialisationSymbol);
	fprintf(ccOut, "\n");
	cTreeTabMap(&s_cTreeTab, generateConstantInitialisation);
	fprintf(ccOut, "\t-1\n};\n\n");

	symbolTabMap(&s_symbolTab, generateCFrontVarDeclaration);

}

