/*
 * recur.c (obsolete)
 *
 *
 * Part of TREE-PUZZLE 5.2 (February 2005)
 *
 * (c) 2003-2005 by Heiko A. Schmidt, Korbinian Strimmer, and Arndt von Haeseler
 * (c) 1999-2003 by Heiko A. Schmidt, Korbinian Strimmer,
 *                  M. Vingron, and Arndt von Haeseler
 * (c) 1995-1999 by Korbinian Strimmer and Arndt von Haeseler
 *
 * All parts of the source except where indicated are distributed under
 * the GNU public licence.  See http://www.opensource.org for details.
 *
 * ($Id$)
 *
 */

/* initialize tree with the following starting configuration (see pstep.h) */
void inittree_recur(ONEEDGE **edge,      /* out: new array of edges          */
                    int     **edgeofleaf,/* out: array of external edge ptrs */
                    int      *rootleaf,  /* out: rooting leaf (=trueID[0])   */
                    int       Maxspc,    /* in:  Number of species (n)       */
                    int       Maxbrnch,  /* in:  Number of branches (2n-3)   */
                    int      *nextedge,  /* out: next free edge index (=3)   */
                    int      *nextleaf,  /* out: next free leaf index (=3)   */
                    int      *trueID)
{
	int i, j;
	ONEEDGE *tmpedge;
	int          *tmpedgeofleaf;

	/* allocate the memory for the whole tree */

	/* allocate memory for vector with all the edges of the tree */
	tmpedge = (ONEEDGE *) calloc((size_t) Maxbrnch, sizeof(ONEEDGE) );
	if (tmpedge == NULL) maerror("edge in inittree");
	*edge = tmpedge;

	/* allocate memory for tmpvector with edge numbers of leaves */
	tmpedgeofleaf = (int *) calloc((size_t) Maxspc, sizeof(int) );
	if (tmpedgeofleaf == NULL) maerror("edgeofleaf in inittree");
	for (j = 0; j < Maxspc; j++) tmpedgeofleaf[j]=-1;
	*edgeofleaf = tmpedgeofleaf;

	/* allocate memory for all the edges the split map */
	for (i = 0; i < Maxbrnch; i++) {
		(tmpedge)[i].cluster = (int *) calloc((size_t) Maxspc, sizeof(int) );
		if (tmpedge[i].cluster == NULL) maerror("edge cluster array in inittree");
		(tmpedge)[i].taxon = -1;
		for (j = 0; j < Maxspc; j++) (tmpedge)[i].cluster[j]=-1;

		/* number all edges */
		tmpedge[i].numedge = i;
	}

	/* initialize tree */

	*nextedge = 3;
	*nextleaf = 3;

	/* init cluster vectors: */
	/*   for edge 0 */
	(tmpedge[0].cluster)[0]      = trueID[1]; /* leaf 1 in edge cluster */
	(tmpedge[0].cluster)[1]      = trueID[2]; /* leaf 2 in edge cluster */
	 tmpedge[0].clsize           = 2;

	/*   for edge 1 */
	(tmpedge[1].cluster)[0]      = trueID[1]; /* leaf 0 in edge cluster */
	 tmpedge[1].clsize           = 1;

	/*   for edge 2 */
	(tmpedge[2].cluster)[0]      = trueID[2]; /* leaf 0 in edge cluster */
	 tmpedge[2].clsize           = 1;

	/* interconnection */
	tmpedge[0].up        = NULL;
	tmpedge[0].downleft  = &tmpedge[1];
	tmpedge[0].downright = &tmpedge[2];

	tmpedge[1].up        = &tmpedge[0];
	tmpedge[1].downleft  = NULL;
	tmpedge[1].downright = NULL;

	tmpedge[2].up = &tmpedge[0];
	tmpedge[2].downleft  = NULL;
	tmpedge[2].downright = NULL;

	/* taxon IDs of leaves */
	tmpedge[0].taxon = trueID[0];
	tmpedge[1].taxon = trueID[1];
	tmpedge[2].taxon = trueID[2];

	/* edges of leaves */
	tmpedgeofleaf[trueID[0]] = 0;
	tmpedgeofleaf[trueID[1]] = 1;
	tmpedgeofleaf[trueID[2]] = 2;

	/* uppest edge =: root edge*/
	*rootleaf = trueID[0];

} /* inittree_recur */

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


/* free memory (to be called after inittree) */
void freetree_recur(ONEEDGE *edge,               /* edge array          */
                   int      *edgeofleaf,         /* ext. edge idx array */
                   int       Maxspc)             /* No. of species      */
{
	int i;

	/* free cluster vectors */
	for (i = 0; i < 2 * Maxspc - 3; i++) {
		free(edge[i].cluster);
	}

	/* free tree topology */
	free(edge);

	/* free external edge lookup vector */
	free(edgeofleaf);

} /* freetree_recur */

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


void updatecluster_recur(ONEEDGE *curredge,    /* current edge               */
                       int      wherefrom,   /* direction to last curr. edge */
                       int      newleaf,     /* leaf to add to clusters      */
                       ONEEDGE *edge,       /* edge array                    */
                       int      Maxspc,      /* No. of species               */
                       int      Maxbrnch,    /* No. of branches              */
                       int     *edgeofleaf,  /* ext. edge idx array          */
                       int      in_nextedge, /* next free edge idx           */
                       int      in_nextleaf) /* next free leaf idx           */
{
	curredge->cluster[(curredge->clsize)++] = newleaf; /* leaf is down */

	if ((curredge->up) != NULL) { /* not root */
		updatecluster_recur(curredge->up, DOWNLEFT, newleaf,
			            edge, Maxspc, Maxbrnch, edgeofleaf, 
		                    in_nextedge, in_nextleaf);
	}
} /* updatecluster_recur */

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


/* add next leaf on the specified edge */
void updatetreeclusters_recur(int      dockedge,    /* dockedge                */
                            int      newleaf,     /* leaf to add to splits   */
                            ONEEDGE *edge,        /* edge array              */
                            int      Maxspc,      /* No. of species          */
                            int     *edgeofleaf,  /* ext. edge idx array     */
                            int      in_nextedge, /* next free edge idx      */
                            int      in_nextleaf, /* next free leaf idx      */
                            ivector  permut)
{
	int      n;		/* counter */
	ONEEDGE *curredge;	/* edge array              */

	/* copy cluster dockedge -> nextedge */
	for (n=0; n < edge[dockedge].clsize; n++) {
		(edge[in_nextedge]).cluster[n] = (edge[dockedge]).cluster[n];
	}

	/* add newleaf new internal edge's (nextedge) cluster */
	(edge[in_nextedge]).cluster[n] = newleaf;
	/* set new internal edge's (nextedge) cluster size */
	(edge[in_nextedge]).clsize = (edge[dockedge]).clsize + 1;

	/* add newleaf new external edge's (nextedge+1) cluster */
	(edge[in_nextedge + 1]).cluster[0] = newleaf;
	(edge[in_nextedge + 1]).clsize = 1;

	curredge = &(edge[in_nextedge]);
	if (curredge->up != NULL) { /* not root */
#if 0
		if (((curredge->up)->downright) == curredge) {
			wherefrom = DOWNRIGHT;
		} else {
			wherefrom = DOWNLEFT;
		}
#endif

		/* climbing up from insertion point */
		updatecluster_recur(curredge->up, DOWNLEFT, newleaf,
				edge, Maxspc, Maxbrnch, edgeofleaf, 
				in_nextedge, in_nextleaf);
	}

} /* updatetreeclusters_recur */

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


#if 0
/* check edge splits */
void check_recur(
                       ONEEDGE *edge,       /* edge array                   */
                       int     Maxspc,      /* No. of species               */
                       int    *edgeofleaf,  /* ext. edge idx array          */
                       int     nextedge, /* next free edge idx           */
                       int     nextleaf, /* next free leaf idx           */
                       int    *permut)
{
	int n,m;

	fprintf(stderr, "Permutation: ");
	for (m = 0; m<nextleaf; m++)
		fprintf(stderr, "%2d:%-2d ", m, permut[m]);
	fprintf(stderr, "+ %2d:%-2d\n", m, permut[m]);


	for (m = 0; m < (nextedge); m++) {
		if (edge[m].upsize + edge[m].downsize != nextleaf) {
			fprintf(stderr, "EEE e[%2d]%c t(%2d)    r<%d | %d>   %d != %d>\n", 
				m, (edge[m].taxon >= 0) ? 'e' : 'i', 
				edge[m].taxon, 
				edge[m].upsize, edge[m].downsize, 
				edge[m].upsize+edge[m].downsize, 
				nextleaf);
			fprintf(stderr, "        UP:");
			for (n = 0; n<edge[m].upsize; n++)
				fprintf(stderr, "%d, ", edge[m].split[n]);
			fprintf(stderr, " | DOWN:");
			for (n = Maxspc - edge[m].downsize; n<Maxspc; n++)
				fprintf(stderr, "%d, ", edge[m].split[n]);
			fprintf(stderr, "\n");
		} else {
			fprintf(stderr, "EEE e[%2d]%c t(%2d)    r<%d | %d>   %d == %d>     OK\n", 
				m, (edge[m].taxon >= 0) ? 'e' : 'i', 
				edge[m].taxon, 
				edge[m].upsize, edge[m].downsize, 
				edge[m].upsize+edge[m].downsize, 
				nextleaf);
		}
	}
} /* check_recur */
#endif

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


#if 0
void updatesplit_split(ONEEDGE *curredge,    /* current edge                 */
                       int      wherefrom,   /* direction to last curr. edge */
                       int      newleaf,     /* leaf to add to splits        */
                       ONEEDGE *edge,       /* edge array                   */
                       int      Maxspc,      /* No. of species               */
                       int      Maxbrnch,    /* No. of branches              */
                       int     *edgeofleaf,  /* ext. edge idx array          */
                       int      in_nextedge, /* next free edge idx           */
                       int      in_nextleaf) /* next free leaf idx           */
{
	switch (wherefrom) {
		case UP:
			curredge->split[(curredge->upsize)++] = newleaf; /* leaf is up */
			if (curredge->downright != NULL) { /* not leaf */
				updatesplit_split(curredge->downright, UP, newleaf,
                       			edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
				updatesplit_split(curredge->downleft, UP, newleaf,
                       			edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
			} else { /* nothing to do */
				return;
			}
			break;
		case DOWNLEFT:
			curredge->split[Maxspc - ++(curredge->downsize)] = newleaf; /* leaf is down */
			if (curredge->downright != NULL) { /* not leaf */
				updatesplit_split(curredge->downright, UP, newleaf,
                       			edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
			}

			if (curredge->up != NULL) { /* not root */
				if ((curredge->up)->downright == curredge) {
					updatesplit_split(curredge->up, DOWNRIGHT, newleaf,
                       				edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
				} else {
					updatesplit_split(curredge->up, DOWNLEFT, newleaf,
                       				edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
				}
			}
			break;
		case DOWNRIGHT:
			curredge->split[Maxspc - ++(curredge->downsize)] = newleaf; /* leaf is down */
			if (curredge->downright != NULL) { /* not leaf */
				updatesplit_split(curredge->downleft, UP, newleaf,
                       			edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
			}

			if ((curredge->up) != NULL) { /* not root */
				if ((curredge->up)->downright == curredge) {
					updatesplit_split(curredge->up, DOWNRIGHT, newleaf,
                       				edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
				} else {
					updatesplit_split(curredge->up, DOWNLEFT, newleaf,
                       				edge, Maxspc, Maxbrnch, edgeofleaf, in_nextedge, in_nextleaf);
				}
			}
			break;
	}
} /* updatesplit_split */

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


/* add next leaf on the specified edge */
void updatetreesplits_split(int      dockedge,    /* dockedge                */
                            int      newleaf,     /* leaf to add to splits   */
                            ONEEDGE *edge,        /* edge array              */
                            int      Maxspc,      /* No. of species          */
                            int     *edgeofleaf,  /* ext. edge idx array     */
                            int      in_nextedge, /* next free edge idx      */
                            int      in_nextleaf, /* next free leaf idx      */
                            ivector  permut)
{
	int      n;		/* counter */
        int      wherefrom;	/* direction to last edge  */
	ONEEDGE *curredge;	/* edge array              */

	/* copy clusters from dockedge to in_nextedge */
	/*   up-cluster: upsize-1 .. 0 */
	for (n=(edge[dockedge].upsize) - 1; n >= 0; n--) {
		(edge[in_nextedge]).split[n] = (edge[dockedge]).split[n];
	}
	(edge[in_nextedge]).upsize = (edge[dockedge]).upsize;

	/*   down-cluster: downsize .. Maxspc-1 */
	for (n=Maxspc - (edge[dockedge]).downsize; n < Maxspc; n++) {
		(edge[in_nextedge]).split[n] = (edge[dockedge]).split[n];
	}
	(edge[in_nextedge]).downsize = (edge[dockedge]).downsize;

	/* add the new taxon to in_nextedge */
	(edge[in_nextedge]).split[Maxspc - ++((edge[in_nextedge]).downsize)] 
		= permut[in_nextleaf];


	/* setup clusters for new external edge (in_nextedge+1) */
	/*   up-cluster: all taxa in tree */
	for (n=0; n < in_nextleaf; n++) {
		(edge[in_nextedge+1]).split[n] = permut[n];
	}
	(edge[in_nextedge+1]).upsize = in_nextleaf;

	/*   down-cluster: the new taxon itself */
	(edge[in_nextedge+1]).split[Maxspc-1] = permut[in_nextleaf];
	(edge[in_nextedge+1]).downsize = 1;

	curredge = &(edge[in_nextedge]);
	if (curredge->up != NULL) { /* not root */
		if (((curredge->up)->downright) == curredge) {
			wherefrom = DOWNRIGHT;
		} else {
			wherefrom = DOWNLEFT;
		}

		/* climbing up from insertion point */
		updatesplit_split(curredge->up, wherefrom, newleaf,
				edge, Maxspc, Maxbrnch, edgeofleaf, 
				in_nextedge, in_nextleaf);
	}

	/* climbing down to dockedge from insertion point */
	updatesplit_split(&(edge[dockedge]), UP, newleaf,
				edge, Maxspc, Maxbrnch, edgeofleaf, 
				in_nextedge, in_nextleaf);


} /* updatetreesplits_split */
#endif

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


/* add next leaf on the specified edge */
void addnextleaf_recur(int      dockedge,        /* insert here         */
                       ONEEDGE *edge,            /* edge array          */
                       int     *edgeofleaf,      /* ext. edge idx array */
                       int      rootleaf,	 /* uppest leaf */
                       int      Maxspc,          /* No. of species      */
                       int      Maxbrnch,        /* No. of branches     */
                       int     *in_nextedge,        /* next free edge idx  */
                       int     *in_nextleaf,        /* next free leaf idx  */
                       ivector  permut)
{
	int nextedge;
	int nextleaf;

	nextedge=*in_nextedge;
	nextleaf=*in_nextleaf;

	if (dockedge >= nextedge) {
		/* Trying to add leaf nextleaf to nonexisting edge dockedge */
		fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR F TO DEVELOPERS\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}

	if (nextleaf >= Maxspc) {
		/* Trying to add leaf nextleaf to a tree with Maxspc leaves */
		fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR G TO DEVELOPERS\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}

	/* necessary change in edgeofleaf if dockedge == root edge */
	if (edgeofleaf[rootleaf] == dockedge) 
		edgeofleaf[rootleaf] = nextedge;

	/* adding nextedge to the tree */
	edge[nextedge].up = edge[dockedge].up;
	edge[nextedge].downleft = &edge[dockedge];
	edge[nextedge].downright = &edge[nextedge+1];
	edge[dockedge].up = &edge[nextedge];

	/* if not root edge: connect ancestor to internal edge (nextedge) */
	if (edge[nextedge].up != NULL) {
		if ( ((edge[nextedge].up)->downleft) == &edge[dockedge] )
			(edge[nextedge].up)->downleft  = &edge[nextedge];
		else
			(edge[nextedge].up)->downright = &edge[nextedge];
	}

	/* adding new external edge (nextedge+1) to the tree */
	edge[nextedge+1].up = &edge[nextedge];
	edge[nextedge+1].downleft = NULL;
	edge[nextedge+1].downright = NULL;
	edge[nextedge+1].taxon = permut[nextleaf];
	edgeofleaf[permut[nextleaf]] = nextedge+1;

#if 0
	updatetreesplits_split(dockedge, permut[nextleaf], edge, Maxspc,
				edgeofleaf, nextedge, nextleaf, permut);
#endif
	updatetreeclusters_recur(dockedge, permut[nextleaf], edge, Maxspc, 
				edgeofleaf, nextedge, nextleaf, permut);

	(*in_nextedge) += 2;
	(*in_nextleaf) ++;

} /* addnextleaf_recur */

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


/* compute edge penalties using splits, if #minedges>1 choose one randomly */
void computepenalties_recur(ONEEDGE  *curredge,    /* edge array          */
                            int       Maxspc,         /* number of taxa      */
                            ulimatrix neighbormatr,   /* neighborhood matrix */
                            ulivector leafpenalty,    /* leaf edge penalties */
                            int      *minedges,   /* minimum edge vector */
                            int      *howmany,    /* number minimum edge */
                            uli      *minpenalty) /* minimum penalty     */
{
	int l, r;
	int lmax, rmax;
	int taxon;
	uli temp;
	ONEEDGE *left;     /* temp edge ptr */
	ONEEDGE *right;    /* temp edge ptr */

	if (curredge->downleft == NULL) {  /* leaf edge */
		taxon                = curredge->taxon;
#if 0
		curredge->cluster[0] = taxon;
		curredge->clsize     = 1;
#endif
		curredge->edgeinfo   = leafpenalty[taxon];
		curredge->allsum     = leafpenalty[taxon];
		curredge->submatrsum    = 0;

		if (*minpenalty == curredge->edgeinfo) { 
			/* another min edge */
			minedges[(*howmany)++] = curredge->numedge;

		} else { /* -> not same minimum */

			if (*minpenalty > curredge->edgeinfo) {
				/* new minimum penalty */
				*howmany = 1;
				minedges[0] = curredge->numedge;
				*minpenalty = curredge->edgeinfo;
			} /* if new minimum */

		} /* same minimum */

	} else {   /* -> internal edge */
		left  = curredge->downleft;
		right = curredge->downright;

		/* descend downleft */
		computepenalties_recur(left, Maxspc,
		                       neighbormatr, leafpenalty,
		                       minedges, howmany,
		                       minpenalty);
		/* descend downright */
		computepenalties_recur(right, Maxspc,
		                       neighbormatr, leafpenalty,
		                       minedges, howmany,
		                       minpenalty);

#if 0
		lmax = left->clsize;
		for (l= 0; l<lmax; l++) {
			/* copy left cluster */
			curredge->cluster[l] = left->cluster[l];
		}
		size = lmax;
#endif

		lmax = left->clsize;
		rmax = right->clsize;
		temp = 0;
		for (r= 0; r<rmax; r++) {
#if 0
			rID = right->cluster[r];
			/* copy right cluster */
			curredge->cluster[size++] = rID;
#endif

			/* sum up sub neighborhood matrix */
			for (l= 0; l<lmax; l++) {
#if 0
				temp += neighbormatr[left->cluster[l]][rID];
#endif
				temp += neighbormatr[left->cluster[l]][right->cluster[r]];
			}
		}
#if 0
		curredge->clsize = size;
#endif

		/* compute edge penalty */
		curredge->submatrsum = 2*temp + 
		                       left->submatrsum + right->submatrsum;
		curredge->allsum = left->allsum + right->allsum;
		curredge->edgeinfo = curredge->allsum - curredge->submatrsum;
		if (*minpenalty == curredge->edgeinfo) { 
			/* another min edge */
			minedges[(*howmany)++] = curredge->numedge;

		} else { /* -> not same minimum */

			if (*minpenalty > curredge->edgeinfo) {
				/* new minimum penalty */
				*howmany = 1;
				minedges[0] = curredge->numedge;
				*minpenalty = curredge->edgeinfo;
			} /* if new minimum */

		} /* same minimum */
;
	}
} /* computepenalties_recur */

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


/* compute edge penalties using splits, if #minedges>1 choose one randomly */
void computepenaltiestree_recur(ONEEDGE  *tmpedge,    /* edge array          */
                            int      *edgeofleaf,     /* external edge ptrs  */
                            int       rootleaf,       /* idx of uppest edge  */
                            int       nextleaf,       /* next free leaf idx  */
                            int       Maxspc,         /* number of taxa      */
                            ulimatrix neighbormatr,   /* neighborhood matrix */
                            ulivector leafpenalty,    /* leaf edge penalties */
                            int      *minedges,       /* minimum edge vector */
                            int      *out_howmany,    /* number minimum edge */
                            uli      *out_minpenalty, /* minimum penalty     */
                            int      *out_minedge)    /* minimum edge        */
{
	int      rootedge;
	int      lmax, rmax;
	int      l, r;
	int      howmany  = 0;
	uli      temp;
	ONEEDGE *left;     /* temp edge ptr */
	ONEEDGE *right;    /* temp edge ptr */
	uli      minpenalty;
	uli      submatrsum;
	uli      allsum;
	uli      edgeinfo;
	ONEEDGE *curredge;

	rootedge = edgeofleaf[rootleaf];
	curredge = &(tmpedge[rootedge]);
	curredge->edgeinfo = leafpenalty[rootleaf];

	/* first edge: init */
	minedges[0]       = rootedge;
	howmany           = 1;
	minpenalty        = curredge->edgeinfo;

	left  = curredge->downleft;
	right = curredge->downright;

	/* descend downleft */
	computepenalties_recur(left, Maxspc,
	                       neighbormatr, leafpenalty,
	                       minedges, &howmany,
	                       &minpenalty);
	/* descend downright */
	computepenalties_recur(right, Maxspc,
	                       neighbormatr, leafpenalty,
	                       minedges, &howmany,
	                       &minpenalty);

	lmax = left->clsize;
	rmax = right->clsize;

	if ((lmax + rmax) != (nextleaf-1)) {
		/* root edge cluster should yield all other leaves */
		fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR HSCL1 TO DEVELOPERS\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}

	temp = 0;
	for (r= 0; r<rmax; r++) {
		/* sum up sub neighborhood matrix */
		for (l= 0; l<lmax; l++) {
			temp += neighbormatr[left->cluster[l]][right->cluster[r]];
		}
	}

	/* compute edge penalty */
	submatrsum = 2*temp + left->submatrsum + right->submatrsum;
	allsum = left->allsum + right->allsum;
	edgeinfo = allsum - submatrsum;


	if ((edgeinfo) != (curredge->edgeinfo)) {
		/* root edge cluster should yield all other leaves */
		fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR HSCL2 TO DEVELOPERS\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}



#if 0 /* optimization (HAS) */
	if (howmany > 1) 
		*out_minedge = minedges[randominteger(howmany)];
	else
		*out_minedge = minedges[0];
#endif

	if (howmany > 1) { /* draw random edge */
		int i, k, randomnum;
		randomnum = randominteger(howmany) + 1; /* 1 to howmany */
		i = -1;
		for (k = 0; k < randomnum; k++) {
			do {
				i++;
			} while (tmpedge[i].edgeinfo != minpenalty);
			*out_minedge = tmpedge[i].numedge;
		}
	} else {
		*out_minedge = minedges[0];
	}

	*out_howmany = howmany;
	*out_minpenalty = minpenalty;

} /* computepenaltiestree_recur */

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


#if 0
/* compute edge penalties using splits, if #minedges>1 choose one randomly */
void computepenalties_split(ONEEDGE  *tmpedge,        /* edge array           */
                            int       nextedge,       /* next free edge idx   */
                            int       Maxspc,         /* number of taxa       */
                            ulimatrix neighbormatr,   /* neighborhood matrix  */
                            int     **out_minedges,   /* minimum edge vector  */
                            int      *out_howmany,    /* number minimum edge  */
                            uli      *out_minpenalty, /* minimum penalty      */
                            int      *out_minedge)    /* minimum edge         */
{
	int  i;                /* counter */
	int  a, b;
	int  idxA, idxB;
	int  maxUP;            /* index counter for right/left split cluster */
	int  howmany  = 0;
	int *minedges = *out_minedges;
	uli  minpenalty;

	for (i = 0; i < nextedge; i++) {
		(tmpedge[i]).penalty = 0;		/* reset penalty */

		maxUP   = (tmpedge[i]).upsize;
		for (idxB = Maxspc - (tmpedge[i]).downsize; idxB < Maxspc; idxB++) {
			b = (tmpedge[i].split)[idxB];
			for (idxA = 0; idxA < maxUP; idxA++) {
				a = (tmpedge[i].split)[idxA];

				(tmpedge[i]).penalty += neighbormatr[a][b];
			} /* for all root-sided (up) taxa */
		} /* for all non-root-sided (down) taxa */

		if (howmany == 0) { /* first edge: init */
			if (minedges == NULL)
				minedges = (int *) calloc((size_t) (2*Maxspc - 3), sizeof(int));

			minedges[howmany++] = i;
			minpenalty = tmpedge[i].penalty;
 		} else { /* -> all other edges */
			if (minpenalty == tmpedge[i].penalty) { 
				/* another min edge */
				minedges[howmany++] = i;

			} else { /* -> not same minimum */

				if (minpenalty > tmpedge[i].penalty) {
					/* new minimum penalty */
					howmany = 0;
					minedges[howmany++] = i;
					minpenalty = tmpedge[i].penalty;
				} /* if new minimum */

			} /* same minimum */
		} /* if all other edges */

	} /* for all edges */

	*out_howmany    = howmany,
	*out_minpenalty = minpenalty;
#if 0
	if (howmany > 1) 
		*out_minedge = minedges[0];
	else
		*out_minedge = minedges[0];
#endif

} /* computepenalties_split */
#endif

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


/* trueID-ed (HAS) */
/* perform one single puzzling step to produce one intermediate tree */
void onepstep_recur(                   /* PStep (intermediate) tree topol:   */
         ONEEDGE **edge,               /*   out: new array of edges          */
         int     **edgeofleaf,         /*   out: array of extern edge ptrs   */
         int      *rootleaf,           /*   out: root leaf in topol, starting perm[0]  */
         unsigned char *quartettopols, /* in: quartetblock with all topols   */
         int       Maxspc,             /* in: Number of species (n)          */
         ivector   permut,             /* in: species permutation (trueID)   */
         int       rootquartsonly_optn) /* in: use root quartets only  */
{

	/* local variables: */
	int  Maxbrnch = (2*Maxspc)-3; /* Number of branches (2n-3)   */
	int  nextedge;                /* next free edge index (=3)   */
	int  nextleaf;                /* next free leaf index (=3)   */
	int  minedge;                 /* insertion edge of minimum penalty */
	uli  mininfo;                 /* minimum penalty   */
	int a, b, c, i;               /* quartet leaves, i to be inserted */
	int idxA, idxB;
	int idxC, idxI;               /* index counter for quartet leaves */
	int chooseX, chooseY;         /* end leaves of penalty path */
	int neighb;                   /* leaf not on penalty path */
	int qstatus                   /* indicator whether quartet in missing/resolved/partly/unresolved */
	ONEEDGE      *tmpedge;        /* new array of edges          */
	int          *tmpedgeofleaf;  /* array of extern edge ptrs   */
	int           tmprootleaf;    /* root edge                   */
	ulimatrix     neighbormatr;
	ulivector     leafpenalty;
	int          *minedges;
	int           howmany;

	neighbormatr = new_ulimatrix(Maxspc,Maxspc);
	leafpenalty  = new_ulivector(Maxspc);

	minedges = (int *) calloc((size_t) (2*Maxspc - 3), sizeof(int));
	if (minedges == NULL) maerror("minimal edge set in onepstep_recur");

	/* allocate and initialize new tree topology */
	inittree_recur(&tmpedge, &tmpedgeofleaf, &tmprootleaf, Maxspc, Maxbrnch, &nextedge, &nextleaf, permut);

	for (idxI = 3; idxI < Maxspc; idxI++) { 

		i = permut[idxI];

		/* initialize penalty neighborhood matrix + leafpenalty vector */
		for (idxA = 0; idxA < nextleaf - 1; idxA++) {
			a = permut[idxA];
			leafpenalty[a] = 0;
			for (idxB = idxA + 1; idxB < nextleaf; idxB++) {
				b = permut[idxB];

				neighbormatr[a][b] = 0;
				neighbormatr[b][a] = 0;
			} 
		} /* for all entries in the upper neighbormatr */

		/*
		 * core of quartet puzzling algorithm
	 	 */

		for (idxA = 0; idxA < nextleaf - 2; idxA++) {
			a = permut[idxA];
			for (idxB = idxA + 1; idxB < nextleaf - 1; idxB++) {
				b = permut[idxB];
				for (idxC = idxB + 1; idxC < nextleaf; idxC++) {
					c = permut[idxC];

					/* check which two leaves out of
					   a,b,c are closer related to each
					   other than to leaf i according to a
					   least squares fit of the continous 
					   Bayesian weights to the seven
					   trivial "attractive regions". We 
					   assign a score of 1 to all edges 
					   on the penalty path between these 
					   two leaves chooseX and chooseY */

					/* de-trueID-ed (HAS) */
					checkquartet(a, b, c, i, &chooseX, &chooseY, &neighb, &qstatus, rootquartsonly_optn);
					(neighbormatr[chooseX][chooseY])++;

				} /* for all quartets q=(a,b,c,i): c */
			} /* for all quartets q=(a,b,c,i): b */
		} /* for all quartets q=(a,b,c,i): a */

		/* symetrize penalty neighborhood matrix */
		for (idxA = 0; idxA < nextleaf - 1; idxA++) {
			a = permut[idxA];
			for (idxB = idxA + 1; idxB < nextleaf; idxB++) {
				b = permut[idxB];

				neighbormatr[a][b] += neighbormatr[b][a];
				neighbormatr[b][a] = neighbormatr[a][b];
				leafpenalty[a] += neighbormatr[a][b];
				leafpenalty[b] += neighbormatr[a][b];
			} 
		} /* for all entries in the upper neighbormatr */

#if 0
		computepenalties_split(tmpedge, nextedge, Maxspc, neighbormatr,
		                       &minedges, &howmany, &mininfo, &minedge);
#endif

		computepenaltiestree_recur(tmpedge, tmpedgeofleaf, tmprootleaf,
                            nextleaf, Maxspc, neighbormatr, leafpenalty,
                            minedges, &howmany, &mininfo, &minedge);

		/* add the next leaf on minedge */
		addnextleaf_recur(minedge, tmpedge, tmpedgeofleaf, tmprootleaf, Maxspc, Maxbrnch, &nextedge, &nextleaf, permut);

	} /* adding all other leafs */

	*edge       = tmpedge;         /* export tree topology             */
	*edgeofleaf = tmpedgeofleaf;   /* export array of extern edge ptrs */
	*rootleaf   = tmprootleaf;     /* export root edge                 */

	free(minedges);
	free_ulimatrix(neighbormatr);
	free_ulivector(leafpenalty);
} /* onepstep_recur */






