/*
 *   pstep.c
 *
 *
 * 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$)
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define EXTERN extern

#include<stdio.h>
#include<stdlib.h>
#include"puzzle.h"

#if PARALLEL
#	include "ppuzzle.h"
#endif
/* #include"util.h" */
/* #include"pstep.h" */

int XXX=0;
/* fprintf(stderr, "XXX: %d (%s:%d)\n", XXX++, __FILE__, __LINE__); */

/******************************************************************************/
/* functions for representing and building puzzling step trees                */
/******************************************************************************/


/******************/
/* de-trueID-ed (HAS) */
/* writes OTU sitting on edge ed (usage: debugging) */
void writeOTU(FILE    *outfp,              /* output file          */
              int      ed,                 /* edge to subtree      */
              ONEEDGE *edge,               /* edge array           */
              int     *edgeofleaf,         /* ext. edge idx array  */
              int      nextedge,           /* next free edge idx   */
              int      nextleaf,           /* next free leaf idx   */
              int     *column)             /* current screen depth */
{
	/* test whether we are on a leaf */
	if (edge[ed].downright == NULL && edge[ed].downleft == NULL) {

		*column += fputid(outfp, edge[ed].taxon);

	} else { /* we are NOT on a leaf */

		fprintf(outfp, "(");
		(*column)++;
		/* de-trueID-ed (HAS) */
		writeOTU(outfp, edge[ed].downleft->numedge, edge, edgeofleaf, nextedge, nextleaf, column);
		fprintf(outfp, ",");
		(*column)++;
		(*column)++;
		if (*column > 55) {
			*column = 2;
			fprintf(outfp, "\n  ");
		}
		/* de-trueID-ed (HAS) */
		writeOTU(outfp, edge[ed].downright->numedge, edge, edgeofleaf, nextedge, nextleaf, column);
		fprintf(outfp, ")");
		(*column)++;
	}
} /* writeOTU */

/******************/
/* de-trueID-ed (HAS) */
/* write tree (usage: debugging) */
void writetree(FILE   *outfp,              /* output file          */
               ONEEDGE *edge,              /* edge array           */
               int     *edgeofleaf,        /* ext. edge idx array  */
               int      nextedge,          /* next free edge idx   */
               int      nextleaf,          /* next free leaf idx   */
               int      rootleaf)          /* root leaf of tree    */
{
	int column;
	column = 1;
	fprintf(outfp, "(");
	column += fputid(outfp, rootleaf) + 3;
	fprintf(outfp, ",");
	/* de-trueID-ed (HAS) */
	writeOTU(outfp, edge[edgeofleaf[rootleaf]].downleft->numedge, edge, edgeofleaf, nextedge, nextleaf, &column);
	column++;
	column++;
	fprintf(outfp, ",");
	/* de-trueID-ed (HAS) */
	writeOTU(outfp, edge[edgeofleaf[rootleaf]].downright->numedge, edge, edgeofleaf, nextedge, nextleaf, &column);
	fprintf(outfp, ");\n");
} /* writetree */


/******************/
/* checks which edge has the lowest edgeinfo
   if there are several edges with the same lowest edgeinfo,
   one of them will be selected randomly */
void minimumedgeinfo(ONEEDGE *edge,        /* edge array           */
                     int     *edgeofleaf,  /* ext. edge idx array  */
                     int      nextedge,    /* next free edge idx   */
                     int      nextleaf,    /* next free leaf idx   */
	             int     *minedges,
	             int     *out_howmany,
                     int     *out_minedge,     /* minimum edge set     */
                     uli     *out_mininfo)     /* minumum penalty      */
{
	int i;
	int howmany;
	int minedge = 0;     /* minimum edge (old search) */
	uli minpenalty = 0;  /* minumum penalty      */

	howmany = 0;

	for (i = 0; i < nextedge; i++) {
		if (howmany == 0) { /* first edge: init */
			minedges[howmany++] = i;
			minpenalty = edge[i].edgeinfo;
 		} else { /* -> all other edges */
			if (minpenalty == edge[i].edgeinfo) { 
				/* another min edge */
				minedges[howmany++] = i;

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

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

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

	}

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

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

	*out_minedge=minedge;
#if 0
#endif

	*out_mininfo=minpenalty;
	*out_howmany=howmany;
} /* minimumedgeinfo */


/******************/
/* clear all edgeinfos */
void resetedgeinfo(ONEEDGE *edge,          /* edge array           */
                   int      nextedge)      /* next free edge idx   */
{
	int i;

	for (i = 0; i < nextedge; i++)
		edge[i].edgeinfo = 0;
} /* resetedgeinfo */

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

/* initialize tree with the following starting configuration
 *               A(=0)
 *               [up       =NULL]
 *               [downleft =1]
 *               [downright=2]
 *
 *                    |
 *                    | 0
 *                    |
 *                   /^\
 *                  *   *
 *                 /     \
 *              1 /       \ 2
 *               /         \ 
 *              /|         |\ 
 *
 *   C(=1)                   B(=2)
 *   [up       =0]           [up       =0]
 *   [downleft =NULL]        [downleft =NULL]
 *   [downright=NULL]        [downright=NULL]
 *
 *   nextedge = 3
 *   nextleaf = 3
 *
 */

/*******************************************************/
/* routines specific for the original O(n^5) algorithm */
/*******************************************************/

#ifdef PSTEP_ORIG

/* de-trueID-ed done (HAS) */
void inittree_orig(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 vetmpctor 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 edge map */
	for (i = 0; i < Maxbrnch; i++) {
		(tmpedge)[i].edgemap = (int *) calloc((size_t) Maxbrnch, sizeof(int) );
		if (tmpedge[i].edgemap == NULL) maerror("edgemap in inittree");
		for (j = 0; j < Maxspc; j++) (tmpedge)[i].edgemap[j]=-1;

		/* number/init all edges */
		tmpedge[i].numedge = i;
		tmpedge[i].taxon   = -1;
	}

	/* initialize tree */

	*nextedge = 3;
	*nextleaf = 3;

	/* edge maps */
	(tmpedge[0].edgemap)[0] = NOWHERE;     /* you are on the right edge */
	(tmpedge[0].edgemap)[1] = DOWNLEFT;    /* go down left for 2nd taxon */
	(tmpedge[0].edgemap)[2] = DOWNRIGHT;   /* go down right for 3rd taxon */

	(tmpedge[1].edgemap)[0] = UP;          /* go up for 1st taxon */
	(tmpedge[1].edgemap)[1] = NOWHERE;     /* you are on the right edge */
	(tmpedge[1].edgemap)[2] = UPDOWNRIGHT; /* go up/down right for 2nd taxon */

	(tmpedge[2].edgemap)[0] = UP;          /* go up for 1st taxon */
	(tmpedge[2].edgemap)[1] = UPDOWNLEFT;  /* go up/down left for 2nd taxon */
	(tmpedge[2].edgemap)[2] = NOWHERE;     /* you are on the right edge */

	/* 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_orig */

/******************/
/* add next leaf on the specified edge */
void addnextleaf_orig(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)          /* permutation vector */
{
	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;

	/* update edge direction maps */
	updateedgemap_orig(dockedge, edge, nextedge);

	nextleaf++;
	nextedge = nextedge + 2;

	*in_nextedge=nextedge;
	*in_nextleaf=nextleaf;
} /* addnextleaf */



/******************/
/* update edgemap */
void updateedgemap_orig(int      dockedge,        /* insert here         */
                        ONEEDGE *edge,            /* edge array          */
                        int      nextedge)        /* next free edge idx  */
{
	int  i;

	/* the two new edges get info about the old edges: */
	/* - nextedge (internal edge) */
	for (i = 0; i < nextedge; i++) {
		switch ( (edge[dockedge].edgemap)[i] ) {

			/* down right changes to down left */
			case DOWNRIGHT:
				(edge[nextedge].edgemap)[i] = DOWNLEFT;
				break;

			/* null changes to down left */
			case NOWHERE:
				(edge[nextedge].edgemap)[i] = DOWNLEFT;
				break;

			/* shouldn't happen */
			case -1:
				fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR G2 TO DEVELOPERS! Unknown direction (leaf %d: %d).\n\n\n", i, (edge[dockedge].edgemap)[i]);
#				if PARALLEL
					PP_Finalize();
#				endif
				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				break;
			default:
				(edge[nextedge].edgemap)[i] = (edge[dockedge].edgemap)[i];
				break;
		} /* switch */
	} /* for i */

	/* - nextedge+1 (external edge) */
	for (i = 0; i < nextedge; i++) {
		switch ( (edge[dockedge].edgemap)[i] ) {

			/* up/down left changes to up */
			case UPDOWNLEFT:
				(edge[nextedge+1].edgemap)[i] = UP;
				break;

			/* up/down right changes to up */
			case UPDOWNRIGHT:
				(edge[nextedge+1].edgemap)[i] = UP;
				break;

			/* down left changes to up/down left */
			case DOWNLEFT:
				(edge[nextedge+1].edgemap)[i] = UPDOWNLEFT;
				break;

			/* down right changes to up/down left */
			case DOWNRIGHT:
				(edge[nextedge+1].edgemap)[i] = UPDOWNLEFT;
				break;

			/* null changes to up/down left */
			case NOWHERE:
				(edge[nextedge+1].edgemap)[i] = UPDOWNLEFT;
				break;

			/* up stays up */
			case UP:
				(edge[nextedge+1].edgemap)[i] = (edge[dockedge].edgemap)[i];
				break;
			/* shouldn't happen */
			default:
				fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR G1 TO DEVELOPERS! Unknown direction (leaf %d: %d).\n\n\n", i, (edge[dockedge].edgemap)[i]);
#				if PARALLEL
					PP_Finalize();
#				endif
				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				break;
		} /* switch */
	} /* for i */

	/* old internal edge (dockedge) */
	for (i = 0; i < nextedge; i++) {
		switch ( (edge[dockedge].edgemap)[i] ) {

			/* up/down right changes to up */
			case UPDOWNRIGHT:
				(edge[dockedge].edgemap)[i] = UP;
				break;

			/* up/down left changes to up */
			case UPDOWNLEFT:
				(edge[dockedge].edgemap)[i] = UP;
				break;

			/* shouldn't happen */
			case -1:
				fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR G3 TO DEVELOPERS! Unknown direction (leaf %d: %d).\n\n\n", i, (edge[dockedge].edgemap)[i]);
#				if PARALLEL
					PP_Finalize();
#				endif
				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				break;

			default:
				break;
		} /* switch */
	} /* for i */

	/* all edgemaps are updated for the two new edges: */
	/* new internal edge (nextedge) */
	(edge[nextedge].edgemap)[nextedge] = NOWHERE;     /* to itself */
	(edge[nextedge].edgemap)[nextedge+1] = DOWNRIGHT; /* to new ext. edge: down right */

	/* new external edge (nextedge+1) */
	(edge[nextedge+1].edgemap)[nextedge]   = UP;      /* to new int. edge: up */
	(edge[nextedge+1].edgemap)[nextedge+1] = NOWHERE; /* to itself */

	/* from all other edges to the new edges */
	for (i = 0; i < nextedge; i++) {
		(edge[i].edgemap)[nextedge]   = (edge[i].edgemap)[dockedge];
		(edge[i].edgemap)[nextedge+1] = (edge[i].edgemap)[dockedge];
	}

	/* an extra: from dockedge to the new edges */
	(edge[dockedge].edgemap)[nextedge]   = UP; /* up */
	(edge[dockedge].edgemap)[nextedge+1] = UPDOWNRIGHT; /* up/down right */

} /* updateedgemap_orig */


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

	for (i = 0; i < 2 * Maxspc - 3; i++) free(edge[i].edgemap);
	free(edge);
	free(edgeofleaf);
} /* freetree */


/******************/
/* increment all edgeinfo between leaf A and B */
/* de-trueID-ed (HAS) nothing done */
void incrementedgeinfo_orig(int      A,         /* start leaf of penalty path */
                            int      B,         /* start leaf of penalty path */
                            ONEEDGE *edge,        /* edge array           */
                            int     *edgeofleaf)  /* ext. edge idx array  */
{
	int curredge, finaledge, nextstep;

	if (A == B) return;

	finaledge = edgeofleaf[B];

	curredge = edgeofleaf[A];
	(edge[curredge].edgeinfo)++;

	while (curredge != finaledge) {
		/* nextstep = (edge[curredge].edgemap)[finaledge]; (HAS) ???*/
		nextstep = (edge[curredge].edgemap)[finaledge];
		switch (nextstep) {

			/* up */
			case UP:
				curredge = (edge[curredge].up)->numedge;
				break;

			/* up/down left */
			case UPDOWNLEFT:
				curredge = ((edge[curredge].up)->downleft)->numedge;
				break;

			/* up/down right */
			case UPDOWNRIGHT:
				curredge = ((edge[curredge].up)->downright)->numedge;
				break;

			/* down left */
			case DOWNLEFT:
				curredge = (edge[curredge].downleft)->numedge;
				break;

			/* down right */
			case DOWNRIGHT:
				curredge = (edge[curredge].downright)->numedge;
				break;

			/* shouldn't happen */
			default:
				fprintf(STDOUT, "\n\n\nHALT: PLEASE REPORT ERROR G4 TO DEVELOPERS! Unknown direction (leaf %d: %d).\n\n\n", finaledge, nextstep);
#				if PARALLEL
					PP_Finalize();
#				endif
				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				break;
		} /* switch */

		(edge[curredge].edgeinfo)++;
	} /* while on path */

} /* incrementedgeinfo_orig */

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


/* trueID-ed (HAS) */
/* perform one single puzzling step to produce one intermediate tree */
void onepstep_orig(                    /* 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 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                   */
	int     *minedges;
	int      howmany;

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

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

	/* adding all other leafs */
	for (i = 3; i < Maxspc; i++) { 

		/* clear all edgeinfos */
		resetedgeinfo(tmpedge, nextedge);

		/*
		 * core of quartet puzzling algorithm
	 	 */

		for (a = 0; a < nextleaf - 2; a++)
			for (b = a + 1; b < nextleaf - 1; b++)
				for (c = b + 1; c < nextleaf; c++) {

					/* 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(permut[a], permut[b], permut[c], permut[i], &chooseX, &chooseY, &neighb, &qstatus, rootquartsonly_optn);
					incrementedgeinfo_orig(chooseX, chooseY, tmpedge, tmpedgeofleaf);

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

		/* find out which edge has lowest edgeinfo */
		minimumedgeinfo(tmpedge, tmpedgeofleaf, nextedge, nextleaf, minedges, &howmany, &minedge, &mininfo);

		/* add the next leaf on minedge */
		addnextleaf_orig(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);
} /* onepstep_orig */

#endif /* PSTEP_ORIG */

/**********************************************************/
/* routines specific for the split-based O(n^4) algorithm */
/**********************************************************/
#ifdef PSTEP_SPLIT
#	include "pstep-split.c"
#endif /* PSTEP_SPLIT */

/********************************************************/
/* routines specific for the recursive O(n^4) algorithm */
/********************************************************/
#ifdef PSTEP_RECUR
#	include "pstep-recur.c"
#endif /* PSTEP_RECUR */

/*********************************************************/
/* routines specific for the MRCA-based O(n^4) algorithm */
/* (suggested by Vincent Berry)                          */
/*********************************************************/
#ifdef PSTEP_MRCA
#	include "pstep-mrca.c"
#endif /* PSTEP_MRCA */

/************************************************************/
/* routines specific for the triplet-based O(n^4) algorithm */
/* incorporating bonus and penalty voting schemes for paths */
/* and subtrees                                             */
/************************************************************/
#ifdef PSTEP_TRIPL
#	include "pstep-tripl.c"
#endif /* PSTEP_TRIPL */




/************************************************************************/
/*  global function of the puzzling step                                */
/************************************************************************/

/* perform Numtrial single puzzling steps constructing Numtrial intermediate */
/* trees, sort each of them and extract splits for consensus step            */
void allpstep(uli       Numtrial,         /* in: no. psteps to perform       */
              unsigned char *quartetinfo, /* in: quartetblock with all topols*/
              int       Maxspc,           /* in: Number of species (n)       */
              int       fixedorder_optn,  /* in: 'fixed' anchored RNG (debug)*/
              int       rootstart_optn,   /* in: start with root quartets    */
              int       rootquartsonly_optn) /* in: use root quartets only  */
{
	/* misc variables */
	ivector       trueIDtmp;       /* species permutation (trueID)     */
	uli           Currtrial;       /* step counter                     */
	FILE         *splitoccurfp; /* file pointer for split occurrences */

#	ifdef PSTEP_BENCH
		time_t benchtime0, benchtime1;
#	endif /* PSTEP_BENCH */
	/* PStep (intermediate) tree topol: */
	ONEEDGE *tmpedgeset;           /*   array of edges                 */
	int          *tmpedgeofleaf;   /*   array of extern edge pointers  */
	int           psteprootleaf;   /*   root edge                      */

	/* for unique sorting of tree topologies */
	int  *ctree;		/* sort tree */
	int   startnode;	/* root in sort tree */
	int   tx;		/* taxon counter */
	char *trstr;		/* phylip tree string */
	treelistitemtype *treeitem; /* list entry of tree */

#	if PARALLEL
		cmatrix *bp;	/* temporary buffer for splits in slaves */
		uli n;		/* step count for init/free split buffer */
#	endif /* PARALLEL */


#	if PARALLEL
		/* add times to arry */
		addtimes(GENERAL, &tarr);

		/* alloc temporary split memory */
		bp = (cmatrix *) calloc((size_t) Numtrial, sizeof(void *));
		for(n=0; n<Numtrial; n++) {
			bp[n] = new_cmatrix(Maxspc - 3, Maxspc);
		}
#	endif /* PARALLEL */

#	ifdef PSTEP_BENCH
		time(&benchtime0);
#	endif /* PSTEP_BENCH */

       	if(savesplitoccurfile_optn){
		openfiletowrite(&splitoccurfp, SPLITOCCUR, "split occurrence file", stdinput_fp);
		fprintf(splitoccurfp, "%s\t%s\t%s\n", "split", "tree", "curroccur");
		fclose(splitoccurfp);
	}

	/* do single puzzling steps Numtrial times */
	trueIDtmp = new_ivector(Maxspc);
	for (Currtrial = 0; Currtrial < Numtrial; Currtrial++) {
		/* randomize input order */
		if (fixedorder_optn) {
			fixedchooser(Maxspc, Maxspc, trueIDtmp);
			if (rootstart_optn) {
				int done=FALSE;
				for (tx=0; (tx<Maxspc && !done); tx++) {
					if (trueIDtmp[tx]==outgroup) {
						trueIDtmp[tx]=0;
						trueIDtmp[0]=outgroup;
						done=TRUE;
					}
				}
			}
		} else {
			if (! readsubset_optn) {
				chooser(Maxspc, Maxspc, trueIDtmp);
				if (rootstart_optn) {
					int done=FALSE;
					for (tx=0; (tx<Maxspc && !done); tx++) {
						if (trueIDtmp[tx]==outgroup) {
							trueIDtmp[tx]=0;
							trueIDtmp[0]=outgroup;
							done=TRUE;
						}
					}
				}
			} else { /* readsubset_optn */
				/* TODO: add rootstart_optn for rssm (HAS ;-) */
#if 0
fprintf(stderr,"Maxspc=%d   Maxsubset=%d\n", Maxspc, Maxsubset);
#endif
				permutetaxa_ss(Maxspc, trueIDtmp, Namestr, 
				               Maxsubset, ss_setovlgraph, 
				               ss_setoverlaps, ss_setovllist, 
				               ss_setovllistsize, ss_matrix, 
				               ss_list, ss_listsize);
			} /* else readsubset_optn */
		}

		/* one single puzzling step */
#		ifdef PSTEP_ORIG
			onepstep_orig(&tmpedgeset, &tmpedgeofleaf, 
				&psteprootleaf, quartetinfo, Maxspc, 
				trueIDtmp, rootquartsonly_optn); 
#		endif /* PSTEP_ORIG */

#		ifdef PSTEP_SPLIT
			onepstep_split(&tmpedgeset, &tmpedgeofleaf, 
				&psteprootleaf, quartetinfo, Maxspc, 
				trueIDtmp, rootquartsonly_optn); 
#		endif /* PSTEP_SPLIT */

#		ifdef PSTEP_RECUR
			onepstep_recur(&tmpedgeset, &tmpedgeofleaf, 
				&psteprootleaf, quartetinfo, Maxspc, 
				trueIDtmp, rootquartsonly_optn); 
#		endif /* PSTEP_RECUR */

#		ifdef PSTEP_MRCA
			onepstep_mrca(&tmpedgeset, &tmpedgeofleaf, 
				&psteprootleaf, quartetinfo, Maxspc, 
				trueIDtmp, rootquartsonly_optn); 
#		endif /* PSTEP_MRCA */

#		ifdef PSTEP_TRIPL
			onepstep_tripl(&tmpedgeset, &tmpedgeofleaf, 
				&psteprootleaf, quartetinfo, Maxspc, 
				trueIDtmp, Currtrial, rootquartsonly_optn); 
#		endif /* PSTEP_TRIPL */

		/* compute bipartitions of current tree */
		/* missing parameters (HAS) */
#		if ! PARALLEL	
			/* de-trueID-ed (HAS) */
			computebiparts(tmpedgeset, tmpedgeofleaf, psteprootleaf,
			               biparts, outgroup, Maxspc); 

			makenewsplitentries(biparts, Maxspc-3, 
			                    &splitpatterns, &splitsizes, 
			                    &splitfreqs, &numbiparts, 
			                    &maxbiparts, Currtrial+1, 
                                            Maxspc, savesplitoccurfile_optn);
#		else /* PARALLEL */
			/* de-trueID-ed (HAS) */
			computebiparts(tmpedgeset, tmpedgeofleaf, psteprootleaf,
			               bp[Currtrial], outgroup, Maxspc); 

			/* file splits not (yet) needed in slaves */
			/* filing is done centrally at the master */
			/* maybe optimizable by presorting (HAS) */
#			if 0
				makenewsplitentries(bp[Currtrial], Maxspc-3, 
				                    &splitpatterns, &splitsizes,
				                    &splitfreqs, &numbiparts, 
				                    &maxbiparts, Maxspc);
#			endif
#		endif /* else PARALLEL */


		/* Sorting to find unique topologies:               */
		/****************************************************/
		/* sort tree topology branches, so that within      */
		/* each pair of sister subtrees the right-most      */
		/* leaf's ID of the right subtree is smaller than   */
		/* the right-most leaf's ID of the lest subtree.    */
		/* Exception: The outgroup (root) is the tree's     */
		/*            right-most leaf!                      */

		/* init/alloc copy tree structure */
		ctree = initctree();
		/* copy tree structure */
		/* de-trueID-ed (HAS) */
		copytree(ctree, tmpedgeset, tmpedgeofleaf,
		         psteprootleaf, Maxspc); 
		/* sort tree, startnode=outgroup */
		startnode = sortctree(ctree);
		/* sorted tree -> phylip tree string */
		trstr=sprintfctree(ctree, psteptreestrlen);

#		if ! PARALLEL
			/* output unique tree to successive list, if option set */
			/* not done on slave processes */
			if(saveptall_optn == PTALL_OUT) {
				FILE *ptf;
				openfiletoappend(&ptf, OUTPTALL, "successive puzzling trees file", stdinput_fp);
				fprintffullpstree(ptf, trstr);
				fprintf(ptf, "\n");
				closefile(ptf);
			}
#		endif /* ! PARALLEL */

		/* add sorted tree to unique tree list */
		treeitem = addtree2list(&trstr, 1, &psteptreelist, &psteptreenum, &psteptreesum);

#		if ! PARALLEL
			/* output unique tree to trace list, if option set */
			/* not done on slave processes */
			if((listqptrees_optn == PSTOUT_LIST) || (listqptrees_optn == PSTOUT_LISTORDER)) {
				/* print: order no/# topol per this id/tree id/sum of unique topologies/sum of trees so far */
				fprintf(qptlist, "%ld.\t1\t%d\t%d\t%d\t%d\n", 
					Currtrial + 1, (*treeitem).count,
				        (*treeitem).id, psteptreenum,
				        psteptreesum);
			}
#		endif /* ! PARALLEL */

#		ifdef VERBOSE1
			printf("%s\n", trstr);
			printfsortedpstrees(psteptreelist);
#		endif

		/* free sorting tree structure */
		freectree(&ctree);

		/* free tree before building the next tree */
#		ifdef PSTEP_ORIG
			freetree_orig(tmpedgeset, tmpedgeofleaf, Maxspc);
#		endif /* PSTEP_ORIG */

#		ifdef PSTEP_SPLIT
			freetree_split(tmpedgeset, tmpedgeofleaf, Maxspc);
#		endif /* STEP_SPLIT */

#		ifdef PSTEP_RECUR
			freetree_recur(tmpedgeset, tmpedgeofleaf, Maxspc);
#		endif /* PSTEP_RECUR */

#		ifdef PSTEP_MRCA
			freetree_mrca(tmpedgeset, tmpedgeofleaf, Maxspc);
#		endif /* PSTEP_MRCA */

#		ifdef PSTEP_TRIPL
			freetree_tripl(tmpedgeset, tmpedgeofleaf, Maxspc);
#		endif /* PSTEP_TRIPL */

#		if ! PARALLEL
			checktime(&time0, &time1, &time2, Currtrial, Numtrial, &mflag);

#		endif /* ! PARALLEL */

		addtimes(PUZZLING, &tarr);
	} /* for Numtrials */
	free_ivector(trueIDtmp);

#	ifdef PSTEP_BENCH
		time(&benchtime1);
		fprintf(stderr,"The %s needed: %.0fs = %fmin for %ld puzzlings on %d taxa\n", 
			PSTEPALGNAME, (double)(benchtime1-benchtime0), 
			(double)(benchtime1-benchtime0)/60.0, Numtrial, Maxspc);
#	endif /* PSTEP_BENCH */

#	if PARALLEL
		/* in slaves: send results: splits, trees */
		PP_SendSplitsBlock(Maxspc, Numtrial, bp, psteptreenum, psteptreelist);

		/* free temporary split memory */
		for (Currtrial = 0; Currtrial < Numtrial; Currtrial++) {
			free_cmatrix(bp[Currtrial]);
		}
		free(bp);

		/* sent! Thus, in slave process not needed any more */
		freetreelist(&psteptreelist, &psteptreenum, &psteptreesum);
#	endif /* PARALLEL */

} /* allpstep */


