Пример #1
0
/* checks if there is an edge between node1 and node2 */
int is_edge(Network *N,int node1,int node2)
{
	//register int i;

	return(MatGet(N->mat,node1,node2));

}
Пример #2
0
/* mA <- mA + alpha*I */
void addId(double alpha, Mat mA) {
  if (MatDev(mA)) {
    cuAddId(alpha, MatElems(mA), MatN(mA), MatElemSize(mA));
  } else for (int diag = 0; diag < MatN(mA); diag++) {
    /* This could be marginally sped up using *axpy with a 1xn
       vector of ones and a stride of n + 1 over the matrix, but
       it's not worth the trouble. */
    MatPut(mA, diag, diag, alpha + MatGet(mA, diag, diag));
  }
}
Пример #3
0
/* Computes the sum of the entries on the main diagonal. */
double trace(Mat mA) {
  double trace;

  if (MatDev(mA)) {
    trace = cuTrace(MatElems(mA), MatN(mA), MatElemSize(mA));
  } else {
    trace = 0.;
    for (int i = 0; i < MatN(mA); i++) {
      trace += MatGet(mA, i, i);
    }
  }

  return trace;
}
Пример #4
0
/* checks if there is an edge between node1 and node2
   if we are at the cross edges s1->t2 or s2->t1 output 1
   considers that a switch means switching putting on
   t2->s1 and t1->s2*/
int is_edge2_dbl(Network *N,int node1,int node2,int s1,int t1,int s2,int t2)
{
	//register int i;

	if (((node1==s1)&&(node2==t2))||((node1==s2)&&(node2==t1)))
		return(1);
	if (((node2==s1)&&(node1==t2))||((node2==s2)&&(node1==t1)))
		return(1);
	if (((node1==s1)&&(node2==t1))||((node1==s2)&&(node2==t2)))
		return(0);
	if (((node2==s1)&&(node1==t1))||((node2==s2)&&(node1==t2)))
		return(0);
	return(MatGet(N->mat,node1,node2));

}
Пример #5
0
/* perform metropolis iteration exchanging double edges. Output the temperature */
double dbl_metrop_switches(Network **RN,int nover,int nlimit,double init_t,int real_vec13[14],int rand_vec13[14])
{

	double t=init_t;
	register int w,k,num_at_t;
	int s1,t1,s2,t2;
	int i,j;
	int sub13[14];
	int add13[14];
	double E1,E2,dE;
	int make_change;
	int tries; //number of tries to find proper pair of edges to exchange
	int twin_i=-1,twin_j=-1;
	w=0;
	if ((w%1000)==0 && GNRL_ST.out_metrop_mat_flag)
		fprintf(GNRL_ST.mat_metrop_fp,"\nBeginning double switches\n");
	k=0,w=0,num_at_t=0;
	t=init_t;

	for (i=1;i<14;i++)
	{
		sub13[i]=0;
		add13[i]=0;
	}

	E1=energy(real_vec13,rand_vec13);

	while ((w<nover)&&(E1>GNRL_ST.e_thresh))
	{
		w++;
		num_at_t++;
		if ((k>nlimit)||(num_at_t>nlimit))
		{
			num_at_t=0;
			k=0;
			t *= TFACTR;		// lower temperature
		}
		w++;
		// random edges to choose : until finding a proper pair of edges which can be candidates
		// should not choose the same edge or the twin one (of the double)
		// try this for 100 times if not getting good candidate then probably
		// there is a only one double edge in the netwprk
		for(i=get_rand((*RN)->e_dbl_num),j=get_rand((*RN)->e_dbl_num),tries=0;
		((j==i) || (j==twin_i)) && (tries<100) ;i=get_rand((*RN)->e_dbl_num),j=get_rand((*RN)->e_dbl_num),tries++);
		//the way out of deadlock
		if(tries==100 || (*RN)->e_dbl_num==2)
			break;
		//this is needed to know if the twin is i-1 or i+1
		if(i & 0x1)
			twin_i=i+1;
		else
			twin_i=i-1;
		if(j & 0x1)
			twin_j=j+1;
		else
			twin_j=j-1;

		s1=(*RN)->e_arr_dbl[i].s;
		t1=(*RN)->e_arr_dbl[i].t;
		s2=(*RN)->e_arr_dbl[j].s;
		t2=(*RN)->e_arr_dbl[j].t;

		//check : 1.that there are no crossing edges in the network,
		//		  2.there are no common vertices of these 2 edges
		//if hasnt passed the check then have to find other pair to switch
		if (( !( MatGet((*RN)->mat,s1,t2) || MatGet((*RN)->mat,s2,t1) || MatGet((*RN)->mat,t1,s2)|| MatGet((*RN)->mat,t2,s1))
			&& (s1!=t2) &&(s2!=t1) && (s1!=s2) && (t1!=t2)) )
		{
			// make fill13_dbl - new function
			fill_13_dbl((*RN),s1,t1,s2,t2,sub13,add13);
			update(rand_vec13,sub13,add13,1);
			E2=energy(real_vec13,rand_vec13);
			dE=E2-E1;
			//	printf("k=%d E=%lf\n",k,E1);
			make_change=metrop(dE,t);

			if (make_change)
			{
				if(DEBUG_LEVEL==1 && GNRL_ST.out_log_flag) {
					fprintf(GNRL_ST.log_fp,"switch #%d: (%d %d) (%d %d)\n", k,s1,t1,s2,t2);
					fprintf(GNRL_ST.log_fp,"i: %d, twin i %d , j: %d twin j : %d\n", i,twin_i,j,twin_j);
				}
				if ((w%1000)==0 && GNRL_ST.out_metrop_mat_flag)
				{
					fprintf(GNRL_ST.mat_metrop_fp,"%lf\n",E1);
					printf("success=%d k=%d E1=%lf T=%lf\n",k,w,E1,t);
				}
				E1=E2;
				if ((w%1000)==0 && GNRL_ST.out_metrop_mat_flag)
						printf("success=%d k=%d E1=%lf T=%lf\n",k,w,E1,t);
				//make the switch
				//update edges array
				(*RN)->e_arr_dbl[i].t=t2;
				(*RN)->e_arr_dbl[twin_i].s=t2;
				(*RN)->e_arr_dbl[j].t=t1;
				(*RN)->e_arr_dbl[twin_j].s=t1;
				if(DEBUG_LEVEL==1 && GNRL_ST.out_log_flag) {
					fprintf(GNRL_ST.log_fp,"(%d %d) (%d %d) (%d %d) (%d %d)\n",
					(*RN)->e_arr_dbl[i].s,(*RN)->e_arr_dbl[i].t,
					(*RN)->e_arr_dbl[twin_i].s,(*RN)->e_arr_dbl[twin_i].t,
					(*RN)->e_arr_dbl[j].s,(*RN)->e_arr_dbl[j].t,
					(*RN)->e_arr_dbl[twin_j].s,(*RN)->e_arr_dbl[twin_j].t
					);
				}

				//update matrix
				MatAsgn((*RN)->mat,s1,t1,0);
				MatAsgn((*RN)->mat,t1,s1,0);
				MatAsgn((*RN)->mat,s2,t2,0);
				MatAsgn((*RN)->mat,t2,s2,0);
				if(DEBUG_LEVEL==1 && GNRL_ST.out_log_flag)
					dump_network(GNRL_ST.log_fp,(*RN));
				MatAsgn((*RN)->mat,s1,t2,1);
				MatAsgn((*RN)->mat,t2,s1,1);
				MatAsgn((*RN)->mat,s2,t1,1);
				MatAsgn((*RN)->mat,t1,s2,1);
				if(DEBUG_LEVEL==1 && GNRL_ST.out_log_flag)
					dump_network(GNRL_ST.log_fp,(*RN));
				if (DEBUG_LEVEL>1 && GNRL_ST.out_metrop_mat_flag)
				{
					fprintf(GNRL_ST.mat_metrop_fp,"%lf %lf  ",E1,t);
					output13(rand_vec13,GNRL_ST.mat_metrop_fp);
				}
				k++;
			}
			else
			/* change rand_vec13 back */
			{
				update(rand_vec13,sub13,add13,0);
			}
		}
	}
	if(GNRL_ST.out_metrop_mat_flag==TRUE){
		fprintf(GNRL_ST.mat_metrop_fp,"%lf\n",E1);
		printf("success=%d k=%d E1=%lf T=%lf\n",k,w,E1,t);
	}
	return(t);
}
Пример #6
0
/* perform metropolis iteration exchanging single edges. Output the temperature */
double sin_metrop_switches(Network **RN,int nover,int nlimit,double init_t,int real_vec13[14],int rand_vec13[14])
{

	double t=init_t;
	register int w,k,num_at_t;
	int s1,t1,s2,t2;
	int i,j,l;
	int sub13[14];
	int add13[14];
	double E1,E2,dE;
	int make_change;
	if(GNRL_ST.out_metrop_mat_flag==TRUE)
		printf("\nBeginning single switches\n");

	k=0,w=0,num_at_t=0;
	t=init_t;

	for (i=1;i<14;i++)
	{
		sub13[i]=0;
		add13[i]=0;
	}

	E1=energy(real_vec13,rand_vec13);

	if ((w%1000)==0 && GNRL_ST.out_metrop_mat_flag)
		fprintf(GNRL_ST.mat_metrop_fp,"\nEsin :\n");

	while ((w<nover)&&(E1>GNRL_ST.e_thresh))
	{
		w++;
		num_at_t++;

		if ((k>nlimit)||(num_at_t>nlimit))
		{
			num_at_t=0;
			k=0;
			t *= TFACTR;		// lower temperature
		}

		//get random indexes for edges
		i=get_rand((*RN)->e_sin_num);
		while ((j=get_rand((*RN)->e_sin_num)) == i);

		s1=(*RN)->e_arr_sin[i].s;
		t1=(*RN)->e_arr_sin[i].t;
		s2=(*RN)->e_arr_sin[j].s;
		t2=(*RN)->e_arr_sin[j].t;

		//check : 1.that there are no crossing edges,
		//		  2.there are no common vertices of these 2 edges
		//if hasnt passed the check then have to find other pair to switch

		if ( !(MatGet((*RN)->mat,s1,t2) || MatGet((*RN)->mat,s2,t1)|| MatGet((*RN)->mat,t2,s1) || MatGet((*RN)->mat,t1,s2))
				&&(s1!=s2) && (s1!=t2) && (t1!=s2) && (t1!=t2) )
		{
			fill_13((*RN),s1,t1,s2,t2,sub13,add13);
			update(rand_vec13,sub13,add13,1);
			E2=energy(real_vec13,rand_vec13);
			dE=E2-E1;
			//	printf("k=%d E=%lf\n",k,E1);
			make_change=metrop(dE,t);


			if (make_change)
			{
				if ((w%1000)==0 && GNRL_ST.out_metrop_mat_flag)
				{
					fprintf(GNRL_ST.mat_metrop_fp,"%lf\n",E1);
					printf("success=%d k=%d E1=%lf T=%lf\n",k,w,E1,t);
				}

				if(DEBUG_LEVEL>1 && GNRL_ST.out_metrop_mat_flag)
				{
					fprintf(GNRL_ST.mat_metrop_fp,"The network :\n\n");
					for (l=1;l<=(*RN)->e_sin_num;l++)
						fprintf(GNRL_ST.mat_metrop_fp,"%d %d %d\n",(*RN)->e_arr_sin[l].t,(*RN)->e_arr_sin[l].s,1);
				}


				E1=E2;
				//make the switch
				//update edges array
				(*RN)->e_arr_sin[i].t=t2;
				(*RN)->e_arr_sin[j].t=t1;
				//update matrix
				MatAsgn((*RN)->mat,s1,t1,0);
				MatAsgn((*RN)->mat,s2,t2,0);
				MatAsgn((*RN)->mat,s1,t2,1);
				MatAsgn((*RN)->mat,s2,t1,1);
				//fprintf(GNRL_ST.mat_metrop_fp,"%lf %lf\n  ",E1,t);
				if(DEBUG_LEVEL>1 && GNRL_ST.out_metrop_mat_flag)
				{
					fprintf(GNRL_ST.mat_metrop_fp,"Changed to  :\n\n");
					for (l=1;l<=(*RN)->e_sin_num;l++)
						fprintf(GNRL_ST.mat_metrop_fp,"%d %d %d\n",(*RN)->e_arr_sin[l].t,(*RN)->e_arr_sin[l].s,1);

					fprintf(GNRL_ST.mat_metrop_fp,"rand_vec : \n");
					output13(rand_vec13,GNRL_ST.mat_metrop_fp);
					fprintf(GNRL_ST.mat_metrop_fp,"- : \n");
					output13(sub13,GNRL_ST.mat_metrop_fp);
					fprintf(GNRL_ST.mat_metrop_fp,"+ : \n");
					output13(add13,GNRL_ST.mat_metrop_fp);
					fprintf(GNRL_ST.mat_metrop_fp,"The network :\n\n");
					for (l=1;l<=(*RN)->e_sin_num;l++)
						fprintf(GNRL_ST.mat_metrop_fp,"%d %d %d\n",(*RN)->e_arr_sin[l].t,(*RN)->e_arr_sin[l].s,1);
					fprintf(GNRL_ST.mat_metrop_fp,"\n\n");
				}
				k++;
			}
			else
			/* change rand_vec13 back */
			{
				update(rand_vec13,sub13,add13,0);

			}
		}
	}
	if(GNRL_ST.out_metrop_mat_flag==TRUE) {
		fprintf(GNRL_ST.mat_metrop_fp,"%lf\n",E1);
		printf("success=%d k=%d E1=%lf T=%lf\n",k,w,E1,t);
	}
	return(t);
}
Пример #7
0
int
gen_rand_network_metrop(Network **RN_p,int real_vec13[14])
{
	int rc=RC_OK;
	int sin_num_of_switchs,dbl_num_of_switchs,num_at_t;
	//int s2,t1,t2;
	int i,j,l;
	int k;  // number of succesful switches
	int w;  // total number of switches
	//int tries; //number of tries to find proper pair of edges to exchange
	//int twin_i,twin_j;
	Network *RN;
	int rand_network_checked=FALSE;
	Res_tbl met_res_tbl;
	//int real_vec13[14];
	int rand_vec13[14];
	//int sub13[14];
	//int add13[14];
	//double E1,E2,dE;
	double E1,E2;
	int	snover,dnover; // maximum # of graph changes at any temperature
	//int nlimit,snlimit,dnlimit; // maximum # of succesful graph changes at any temperature
	int snlimit,dnlimit; // maximum # of succesful graph changes at any temperature
	//int make_change;
	//int numsucc; // how many successful switches already made
	double t;  // the temperature
	int success;
	int dispair=0;
	double dummy;
	int switches_range;
	double *switch_ratio_arr;
	switch_ratio_arr=(double*)calloc(GNRL_ST.rnd_net_num+1,sizeof(double));

	sin_num_of_switchs=0;
	if(G_N->e_sin_num<=1)
			sin_num_of_switchs=0;
		else {
			// num of switches is round(10*(1+rand)*#edges)
			//num of edges in undirfecetd is actually *2 therefore divided by 2
			switches_range=(int)(2*GNRL_ST.r_switch_factor*G_N->e_sin_num);
			sin_num_of_switchs=(int)switches_range+get_rand((int)switches_range);
		}
	if(G_N->e_dbl_num<=1)
			dbl_num_of_switchs=0;
		else {
			// num of switches is round(10*(1+rand)*#edges)
			//num of edges in undirfecetd is actually *2 therefore divided by 2
			switches_range=(int)(2*GNRL_ST.r_switch_factor*G_N->e_dbl_num/2);
			dbl_num_of_switchs=(int)switches_range+get_rand((int)switches_range);
		}

	t=GNRL_ST.t_init;

	if (GNRL_ST.use_stubs_method==TRUE)
	{

		success=0;
		dispair=0;
		while ((success==FALSE)&&(dispair<GEN_NETWORK_DISPAIR))
		{
			//printf("trying ron\n");
			rc=gen_rand_network_stubs_method(&RN);
			if(rc==RC_OK)
				success=TRUE;
			else
				success=FALSE;
			if (success==FALSE)
			{
				//printf("dispair - discard\n");
				dispair++;
			}

		}

		if (dispair==GEN_NETWORK_DISPAIR) // give up try switch method
			gen_rand_network_switches_method_conserve_double_edges(&RN,&dummy);
		else
		{
			success=update_network(RN,G_N);
			if (!success)
			{
				printf("Error - degrees don't match\n");
				at_exit(-1);
			}

			//shuffle_double_edges(RN);

			if (DEBUG_LEVEL>20 && GNRL_ST.out_log_flag)
			{
				fprintf(GNRL_ST.log_fp,"The network :\n\n");
				for (l=1;l<=(*RN).edges_num;l++)
					fprintf(GNRL_ST.log_fp,"%d %d %d\n",(*RN).e_arr[l].t,(*RN).e_arr[l].s,1);
			}
		}
	}

	else

		gen_rand_network_switches_method_conserve_double_edges(&RN, &dummy);



	/**************************************************************/
	/**************************************************************/
	/**************************************************************/
	/* Part II - metropolis algorithm. Perform switches with probability*/
	/**************************************************************/
	/**************************************************************/
	/**************************************************************/

	/**************************************************************/
		/* metropolis parameters depend on num_of _switches*/


	/* otuput real network to _METROP file */



	init_res_tbl(&met_res_tbl);
	list64_init(&met_res_tbl.real);
	met_motifs_search_real(RN,&met_res_tbl,rand_vec13);
	list64_free_mem(met_res_tbl.real);

	E1=energy(real_vec13,rand_vec13);

	w=0;
	if(GNRL_ST.out_metrop_mat_flag) {
		fprintf(GNRL_ST.mat_metrop_fp,"\nreal network\n");
		fprintf(GNRL_ST.mat_metrop_fp,"E=%lf T=%lf\n",E1,t);
		fprintf(GNRL_ST.mat_metrop_fp,"Real triadic census :\n ");
		output13(real_vec13,GNRL_ST.mat_metrop_fp);
		fprintf(GNRL_ST.mat_metrop_fp,"random network\n");
		fprintf(GNRL_ST.mat_metrop_fp,"%lf,%lf \n",E1,t);
		fprintf(GNRL_ST.mat_metrop_fp,"Initial random triadic census :\n ");
		output13(rand_vec13,GNRL_ST.mat_metrop_fp);
	}

/*	nover=sin_num_of_switchs*GNRL_ST.iteration_factor;
	nlimit=nover/10;
	k=0;
	num_at_t=0;
	sin_metrop_switches(&RN,nover,nlimit,GNRL_ST.t_init,real_vec13,rand_vec13);
*/
	if (dbl_num_of_switchs==0)
	{
		snover=sin_num_of_switchs*(int)GNRL_ST.iteration_factor;
		snlimit=snover/10;
		t=sin_metrop_switches(&RN,snover,snlimit,GNRL_ST.t_init,real_vec13,rand_vec13);
	}
	else
	{
		dnover=dbl_num_of_switchs*(int)GNRL_ST.iteration_factor/10;
		dnlimit=dnover/10;
		if (dnover<10)
		{

			dnover=dbl_num_of_switchs;
			dnlimit=dnover;
		}
		t=dbl_metrop_switches(&RN,dnover,dnlimit,GNRL_ST.t_init,real_vec13,rand_vec13);

		snover=sin_num_of_switchs*(int)GNRL_ST.iteration_factor/10;
		snlimit=snover/10;
		k=0;
		num_at_t=0;
		t=sin_metrop_switches(&RN,snover,snlimit,t,real_vec13,rand_vec13);

		if(GNRL_ST.out_metrop_mat_flag) {
			fprintf(GNRL_ST.mat_metrop_fp,"End E\n");
			printf("End E\n");
		}
		for (i=1;i<10;i++)
		{
			E2=energy(real_vec13,rand_vec13);
			if (E2>0)
			{
				t=dbl_metrop_switches(&RN,dnover,dnlimit,t,real_vec13,rand_vec13);
				t=sin_metrop_switches(&RN,snover,snlimit,t,real_vec13,rand_vec13);
			}
		}
	}

	//check that there are no self edges. If there are any then start again
	rand_network_checked = TRUE;
	if(GNRL_ST.calc_self_edges == FALSE){
		for(i=1;i<=RN->vertices_num;i++) {
			if( MatGet(RN->mat,i,i)==1 ) {
				printf("Self edges still exist building random graph again\n");
				rand_network_checked=FALSE;
				free_network_mem(RN);
				break;
			}
		}
	}

	//merge lists to RN->e_arr
	//
	j=0;
	for(i=1;i<=RN->e_dbl_num;i++) {
		RN->e_arr[++j].s=RN->e_arr_dbl[i].s;
		RN->e_arr[j].t=RN->e_arr_dbl[i].t;
	}
	for(i=1;i<=RN->e_sin_num;i++) {
		RN->e_arr[++j].s=RN->e_arr_sin[i].s;
		RN->e_arr[j].t=RN->e_arr_sin[i].t;
	}

	if(DEBUG_LEVEL>1 && GNRL_ST.out_metrop_mat_flag) {
		fprintf(GNRL_ST.mat_metrop_fp,"The network :\n");
		for (i=1;i<=RN->edges_num;i++)
			fprintf(GNRL_ST.mat_metrop_fp,"%d %d %d\n",RN->e_arr[i].t,RN->e_arr[i].s,1);
		fprintf(GNRL_ST.mat_metrop_fp,"End of network\n");
		fprintf(GNRL_ST.mat_metrop_fp,"\n\n");
	}
	if(GNRL_ST.out_metrop_mat_flag) {
		fprintf(GNRL_ST.mat_metrop_fp,"Real triadic census :\n ");
		output13(real_vec13,GNRL_ST.mat_metrop_fp);
		fprintf(GNRL_ST.mat_metrop_fp,"Final random triadic census :\n ");
		output13(rand_vec13,GNRL_ST.mat_metrop_fp);
	}

	*RN_p=RN;
	return RC_OK;
}
Пример #8
0
static FUNC *ConvertSurface( FUNC *func, OPTIC *optic )
    {
    FUNC *loop ;
    EXPR *EA0, *separate ;
    int ea0used, which, rev ;

    optic->terms[ TERM_MAT ].E = Copy( MatGet() ) ;
    optic->terms[ TERM_TEMP ].E = Copy( MatGetTemp() ) ;

    for( loop = func ; loop ; loop = OpticForward( FuncNext(loop) ) )
	{
	which = (int) ( ExprValue( FuncArg( loop, 0 ) ) + 0.49 ) ;

	if( which < 6 )
	    break ;

	EA0 = Copy( FuncArg( loop, 1 ) ) ;
	ea0used = 0 ;

	switch( which )
	    {
	    case 6:				/* INDX */
		if( FuncArgCount( loop ) == 4 )
		    {
		    MatPush( EA0, FuncArg( loop, 2 ),
			FuncIsReversed( loop ) ) ;
		    optic->mutant = 1 ;
		    (optic - 1)->mutant = -1 ;		/* kludge-o-rama */
		    optic->terms[TERM_MUTANT].E = Copy( FuncArg( loop, 3 ) ) ;
		    }
		else
		if( FuncArgCount( loop ) == 3 )
		    {
		    MatPush( EA0, FuncArg( loop, 2 ),
			FuncIsReversed( loop ) ) ;
		    }
		else
		if( FuncArgCount( loop ) == 2 )
		    {
		    MatPush( EA0, Label( "MatTemperature" ),
			FuncIsReversed( loop ) ) ;
		    }
		break ;
	    case 7:				/* PARAX */
		optic->terms[TERM_FOCLEN].E = EA0 ;
		optic->function |= FUNCTION_PARAX ;
		ea0used = 1 ;
		break ;
	    case 8:				/* MIRROR */
		optic->function |= FUNCTION_MIRROR ;
		break ;
	    case 9:				/* GRATING */
		optic->function |= FUNCTION_GRATING ;
		optic->terms[TERM_LINES].E = EA0 ;
		optic->terms[TERM_ORDER].E = Copy( FuncArg( loop, 2 ) ) ;
		ea0used = 1 ;
		break ;
	    case 10:				/* VIEW */
		optic->function |= FUNCTION_VIEW ;
		break ;
	    case 11:				/* STOP */
		if( FuncArgCount( loop ) == 1 )			/* EXIT */
		    optic->function |= FUNCTION_EXIT ;
		else
		if( FuncArgCount( loop ) == 7 )			/* STOP */
		    {
		    int i ;
		    EDAT *S ;

		    if( optic->StopCount == NSTOPS )
			{
			IOerror( IO_ERR, "ConvertSurface", "too many stops" ) ;
			failed = 1 ;
			break ;
			}

		    S = &optic->terms[ TERM_STOPS + 6 * optic->StopCount ] ;

		    for( i = 0 ; i < 6 ; i++ )
			S[i].E = Copy( FuncArg( loop, i + 1 ) ) ;
		    optic->StopCount++ ;
		    }
		break ;
	    case 12:				/* PDIST */
		if( FuncIsReversed( loop ) )
		    optic->terms[TERM_PDIST].E = Ecc( EA0, "u-", Garbage() ) ;
		else
		optic->terms[TERM_PDIST].E = EA0 ;
		optic->function |= FUNCTION_PDIST ;
		ea0used = 1 ;
		break ;
	    case 13:				/* PRINT */
		break ;
	    case 14:				/* LABEL */
		separate = String( " & " ) ;
		if( optic->terms[ TERM_LABEL ].E )
		    {
		    optic->terms[ TERM_LABEL ].E = Ecc(
			optic->terms[ TERM_LABEL ].E, "//", separate ) ;
		    optic->terms[ TERM_LABEL ].E = Ecc(
			optic->terms[ TERM_LABEL ].E, "//", EA0 ) ;
		    }
		else
		    optic->terms[ TERM_LABEL ].E = EA0 ;
		ea0used = 1 ;
		break ;
	    case 15:				/* SURF */

		rev = ( FuncIsReversed( loop ) ? -1 : 1 ) ;
		if( optic->reverse != rev )
		    {
		    if( !optic->reverse )
			optic->reverse = rev ;
		    else
			{
			IOerror( IO_ERR, "ConvertSurface",
			    "inconsistent surface reversal on surface #%d",
			    Surface ) ;
			failed = 1 ;
			}
		    }

		if( FuncArgCount( loop ) == 4 )
		    {
		    optic->surface |= SURFACE_SYMBOLIC ;
		    optic->terms[TERM_Sx].E = Copy( FuncArg( loop, 1 ) ) ;
		    optic->terms[TERM_Sy].E = Copy( FuncArg( loop, 2 ) ) ;
		    optic->terms[TERM_Sf].E = Copy( FuncArg( loop, 3 ) ) ;

		    if( !ExprIsVariable( optic->terms[TERM_Sx].E ) )
			IOerror( IO_ERR, "ConvertSurface", "X not a variable" ) ;
		    if( !ExprIsVariable( optic->terms[TERM_Sy].E ) )
			IOerror( IO_ERR, "ConvertSurface", "Y not a variable" ) ;

		    break ;
		    }

		optic->terms[TERM_CURV].E = EA0 ;
		ea0used = 1 ;
		if( FuncArgCount( loop ) == 1 )
		    optic->surface |= SURFACE_FLAT ;
		else
		if( FuncArgCount( loop ) == 2 )
		    optic->surface |= SURFACE_SPHERE ;
		else
		if( FuncArgCount( loop ) == 3 )
		    {
		    optic->surface |= SURFACE_CONIC ;
		    optic->terms[TERM_CONIC].E = Copy( FuncArg( loop, 2 ) ) ;
		    }
		else		/* count = 6 */
		    {
		    optic->surface |= SURFACE_ASPHERE ;
		    optic->terms[TERM_CONIC].E = Copy( FuncArg( loop, 2 ) ) ;
		    optic->terms[TERM_A4].E = Copy( FuncArg( loop, 3 ) ) ;
		    optic->terms[TERM_A6].E = Copy( FuncArg( loop, 4 ) ) ;
		    optic->terms[TERM_A8].E = Copy( FuncArg( loop, 5 ) ) ;
		    optic->terms[TERM_A10].E = Copy( FuncArg( loop, 6 ) ) ;
		    }
		break ;
	    case 16:				/* TRIX */

		rev = ( FuncIsReversed( loop ) ? -1 : 1 ) ;
		if( optic->reverse != rev )
		    {
		    if( !optic->reverse )
			optic->reverse = rev ;
		    else
			{
			IOerror( IO_ERR, "ConvertSurface",
			    "inconsistent surface reversal on surface #%d",
			    Surface ) ;
			failed = 1 ;
			}
		    }

		optic->terms[TERM_Ex].E = EA0 ;
		optic->terms[TERM_Ey].E = Copy( FuncArg( loop, 2 ) ) ;
		optic->terms[TERM_Ez].E = Copy( FuncArg( loop, 3 ) ) ;
		optic->terms[TERM_Eo].E = Copy( FuncArg( loop, 4 ) ) ;
		optic->surface |= SURFACE_TRIAXIAL ;
		ea0used = 1 ;
		break ;

	    case 17:				/* MARK */
		if( FuncArgCount( loop ) == 2 )		/* surface */
		    {
		    optic->terms[TERM_MARK].E = EA0 ;
		    ea0used = 1 ;
		    }
		else
		if( FuncArgCount( loop ) == 3 )		/* variable */
		    {
		    if( !ExprIsVariable( FuncArg( loop, 1 ) ) )
			{
			IOerror( OpticBomb, "ConvertSurface",
			    "EXPR not a VARIABLE" ) ;
			}
		    ExprSetFlags( FuncArg( loop, 1 ),
			(int) ExprValue( FuncArg( loop, 2 ) ) & VAR_OPTIC ) ;
		    }
		break ;
	    case 18:				/* COAT */
		if( optic->terms[ TERM_COATING ].E )
		    {
		    IOerror( IO_ERR, "ConvertSurface",
			"multiple coating declarations on surface %d",
			Surface ) ;
		    failed = 1 ;
		    }
		else
		    {
		    optic->terms[ TERM_COATING ].E = EA0 ;
		    ea0used = 1 ;
		    }
		break ;
	    case 19:				/* BOUND */
		break ;
	    case 20:				/* BAFFLE */
		optic->function |= FUNCTION_BAFFLE ;
		break ;
	    case 21:				/* MERIT */
		break ;
	    case 22:				/* APERTURE */
		optic->function |= FUNCTION_APERTURE ;
		if( aperture_set )
		    {
		    IOerror( IO_ERR, "ConvertSurface", "multiple apertures" ) ;
		    failed = 1 ;
		    }
		aperture_set = 1 ;
		break ;
	    case 23:				/* PINHOLE */
		optic->function |= FUNCTION_PINHOLE ;
		break ;
	    case 24:				/* ORIGIN */
		if( origin_set )
		    {
		    IOerror( IO_WARN, "ConvertSurface", "multiple origins" ) ;
/* EVIL HACK!!!! */
/*		    failed = 1 ; */
		    }
		else
		    {
		    optic->function |= FUNCTION_ORIGIN ;
		    origin_set = 1 ;
		    }
		break ;
	    case 25:					/* FRESNEL */
		rev = ( FuncIsReversed( loop ) ? -1 : 1 ) ;
		if( optic->reverse != rev )
		    {
		    if( !optic->reverse )
			optic->reverse = rev ;
		    else
			{
			IOerror( IO_ERR, "ConvertSurface",
			    "inconsistent surface reversal on surface #%d",
			    Surface ) ;
			failed = 1 ;
			}
		    }

		optic->surface |= SURFACE_FRESNEL ;
		optic->terms[TERM_FOCLEN].E = EA0 ;
		ea0used = 1 ;
		break ;
	    default:
		IOerror( OpticBomb, "ConvertSurface",
		    "unexpected surface function on surface #%d", Surface ) ;
		failed = 1 ;
	    }

	if( !ea0used )
	    Free( EA0 ) ;
	}

    if( !Compare( optic->terms[ TERM_MAT ].E, MatGet() ) )
	optic->function |= FUNCTION_SNELL ;

    return loop ;
    }