/*
 * ml2.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

/* prototypes */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "puzzle.h"
#include "util.h"
#include "ml.h"

#define STDOUT     stdout
#ifndef PARALLEL             /* because printf() runs significantly faster */
                             /* than fprintf(stdout) on an Apple McIntosh  */
                             /* (HS) */
#       define FPRINTF    printf
#       define STDOUTFILE
#else
#       define FPRINTF    fprintf
#       define STDOUTFILE STDOUT,
	void PP_Finalize();
#endif

#define CHAR_PER_TREENODE 128 /* was 53 before HAS ;-) */

/* prototypes for two functions of puzzle2.c */
void fputid10(FILE *, int);
int fputid(FILE *, int);


/******************************************************************************/
/* user tree input                                                            */
/******************************************************************************/

/* read/convert branch length from string stream itfp */
double freadedgelength(double *length, char *ch, FILE *itfp)
{
  long    digit, ordzero;
  int     dotread;
  double  divisor; 
  int     minusread;
  
  ordzero   = '0';
  dotread   = FALSE;
  minusread = FALSE;
  *length = 0.0;
  divisor = 1.0;
  *ch = fgetc(itfp); 
  digit = (long)(*ch - ordzero);
  while ( ((digit <= 9) && (digit >= 0)) || *ch == '.' || *ch == '-' ) {
    if (*ch == '.' )
      dotread = TRUE;
    else if (*ch == '-' )
      minusread = TRUE;
    else {
      *length = *length * 10.0 + digit;
      if (dotread) 
	divisor *= 10.0;
    }
    ch++; 
    *ch = fgetc(itfp); 
    digit = (long)(*ch - ordzero);
  }
  
  *length *= divisor;
  
  if (minusread)
    *length = -(*length);
  
  return (*length);

}  /* freadedgelength */


/* read/convert branch length from string *ch */
double sreadedgelength(double *length, char **ch)
{
  long    digit, ordzero;
  int     dotread;
  double  divisor;
  int     minusread;

  ordzero   = '0';
  dotread   = FALSE;
  minusread = FALSE;
  *length = 0.0;
  divisor = 1.0;
  /* **ch = fgetc(itfp); */
  if (**ch == ':') (*ch)++;
  digit = (long)((**ch) - ordzero);
  while ( ((digit <= 9) && (digit >= 0)) || **ch == '.' || **ch == '-' ) {
    if (**ch == '.' )
      dotread = TRUE;
    else if (**ch == '-' )
      minusread = TRUE;
    else {
      *length = *length * 10.0 + digit;
      if (dotread) 
	divisor *= 10.0;
    }
    (*ch)++; 
    /* *ch = fgetc(itfp); */
    digit = (long)((**ch) - ordzero);
  }
  
  *length *= 100;
  *length /= divisor;
  
  if (minusread)
    *length = -(*length);
  
  return (*length);

}  /* sreadedgelength */


void fremovecomment(FILE *itfp, char *ci)
{
	int  comment;

	if (*ci == '[') comment = 1;
	else {
		comment = 0;
		return;
	}

	do {
		*ci = fgetc(itfp);
		if (*ci == EOF) {
			fprintf(STDOUT, "\n\n\nUnable to proceed (missing ';' or ',' in tree)\n\n\n");
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}
		if ((*ci == ']') && comment) {
			comment = 0;
			*ci = fgetc(itfp);
		}
		if (*ci == '[') comment = 1;
	} while (comment);

} /* fremovecomment */





/* read user tree, drop all blanks, tabs, and newlines.
   Drop edgelengths (after :) but keep internal
   labels. Check whether all pairs of brackets match. */
void getusertree(FILE *itfp, cvector tr, int maxlen, int usebranch)
{
	int n;
	int brac;
	char ci;

	/* look for opening bracket */
	n = 0;
	brac = 0;
	do {
		ci = fgetc(itfp);
		if (ci == EOF) {
			fprintf(STDOUT, "\n\n\nUnable to proceed (missing start bracket in tree)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}
		if (ci == '[') fremovecomment(itfp, &ci);
	} while ((ci != '('));
	tr[n] = ci;
	brac++;
	
	do {
		/* get next character (skip blanks, newlines, and tabs) */
		do {
			ci = fgetc(itfp);
			if (ci == EOF) {
				fprintf(STDOUT, "\n\n\nUnable to proceed (no more characters in tree)\n\n\n");
#				if PARALLEL
					PP_Finalize();
#				endif
   				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
			}
			if (ci == '[') fremovecomment(itfp, &ci);
		} while (isspace((int) ci));

		/* remove edgelengths, if not needed (HAS) */
		if (ci == ':') { /* skip characters until a ,) appears  */
			if (!usebranch) {
				do {
					ci = fgetc(itfp);
					if (ci == EOF) {
						fprintf(STDOUT, "\n\n\nUnable to proceed (missing ';' or ',' in tree)\n\n\n");
#						if PARALLEL
							PP_Finalize();
#						endif
   						tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
					}
					if (ci == '[') fremovecomment(itfp, &ci);
				} while ((ci != ',' && ci != ')') );
  
			} else { /* if usebranch */
				n++;
				tr[n] = ci; /* add ':' to string */
				do {
					ci = fgetc(itfp);
					if (ci == EOF) {
						fprintf(STDOUT, "\n\n\nUnable to proceed (missing ';' or ',' in tree)\n\n\n");
   						tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
					}
					if (ci == '[') fremovecomment(itfp, &ci);
					if(!isspace((int) ci) && (ci != ',') && (ci != ')')) {
						n++;
						tr[n] = ci;
					};
				} while ((ci != ',' && ci != ')') );
			}
		}
		
		if (ci == '(') {
			brac++;
		}
		if (ci == ')') {
			brac--;
		}

		n++;
		tr[n] = ci;
	
	} while ((ci != ';') && (n != maxlen-2));
	
	if (n == maxlen-2) {
		fprintf(STDOUT, "\n\n\nUnable to proceed (tree description too long)\n\n\n");
		fprintf(STDOUT, ">>%s<<\n\n\n", tr);

		fprintf(STDOUT, " %d(n) == %d(maxlen-2) \n", n, maxlen-2);
#		if PARALLEL
			PP_Finalize();
#		endif
   		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}
	
	if (brac != 0) {
		fprintf(STDOUT, "\n\n\nUnable to proceed (brackets don't match in tree)\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
   		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}
	
	n++;
	tr[n] = '\0';
} /* getusertree */


Node *internalnode(Tree *tr, char **chpp, int *ninode)
{
	Node  *xp;
	Node  *np;
	Node  *rp;
	int    i, j;
	int    dvg;
	int    ff;
	int    stop;
	int    numc;
	char   ident[100];
        char   idcomp[11];
	char  *idp;
	double blen;

	(*chpp)++;
	if (**chpp == '(') { /* process subgroup */
		
		xp = internalnode(tr, chpp, ninode);
		xp->isop = xp;
		dvg = 1;
		while (**chpp != ')') {
			if (**chpp == '\0') {
				fprintf(STDOUT, "\n\n\nUnable to proceed (unexpected end of tree)\n\n\n");
#				if PARALLEL
					PP_Finalize();
#				endif
   				tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
			}
			dvg++;
			/* insert edges around node */
			np = internalnode(tr, chpp, ninode);
			np->isop = xp->isop;
			xp->isop = np; 
			xp = np;
		}
		/* closing bracket reached */
		/* read edgelength !!!! (HAS) */
		
		(*chpp)++;
		if (dvg < 2) {
			fprintf(STDOUT, "\n\n\nUnable to proceed (only one OTU inside pair of brackets)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}
		
		if ((*ninode) >= Maxspc-3) { /* all internal nodes already used */
			fprintf(STDOUT, "\n\n\nUnable to proceed (no unrooted tree)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}

		rp = tr->ibrnchp[*ninode];
		rp->isop = xp->isop;
		xp->isop = rp;

		for (j = 0; j < Numspc; j++)
			rp->paths[j] = 0;
		xp = rp->isop;
		while (xp != rp) {
			for (j = 0; j < Numspc; j++) {
				if (xp->paths[j] == 1)
					rp->paths[j] = 1;
			}
			xp = xp->isop;
		}
		(*ninode)++;

		if ((**chpp) == ',' || (**chpp) == ')') return rp->kinp;
		if ((**chpp) == '\0')  {
			fprintf(STDOUT, "\n\n\nUnable to proceed (unexpected end of tree)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}

		if ((**chpp) != ':') {

			/* read internal label into rp->label (max. 20 characters) */
			rp->label = new_cvector(21);
			(rp->label)[0] = '\0';
			for (numc = 0; ((numc < 20) && ((**chpp) != ':')); numc++) {
				if ((**chpp) == ',' || (**chpp) == ')') return rp->kinp;
				if ((**chpp) == '\0')  {
					fprintf(STDOUT, "\n\n\nUnable to proceed (unexpected end of tree)\n\n\n");
   					tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				}
				(rp->label)[numc] = **chpp;
				(rp->label)[numc+1] = '\0';
				(*chpp)++;
			}

			/* skip the rest of the internal label */
			while (((**chpp) != ',') && ((**chpp) != ')') && ((**chpp) != ':')) {
				(*chpp)++;
				if ((**chpp) == '\0')  {
					fprintf(STDOUT, "\n\n\nUnable to proceed (unexpected end of tree)\n\n\n");
   					tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				}
			}
		} /* if ! ':' */

		/* read edgelength !!!! (HAS) */
		if (**chpp == ':') {
			(void) sreadedgelength(&blen, chpp);
			rp->lengthext=blen;
			rp->lengthset=1;
			rp->kinp->lengthext=blen;
			rp->kinp->lengthset=1;
		}


		return rp->kinp;

	} else { /* process species names */
		/* read species name */
		for (idp = ident; 
		     **chpp != ',' && **chpp != ')' && **chpp != ':' && **chpp != '\0';
		     (*chpp)++) {
			*idp++ = **chpp;	
		}
		*idp = '\0';

		/* look for internal number */
		idcomp[10] = '\0';
		
		for (i = 0; i < Maxspc; i++) {
			ff = 0;
			stop = FALSE;
			do {
				idcomp[ff] = Identif[i][ff];
				ff++;
				if (idcomp[ff-1] == ' ') stop = TRUE;
			} while (!stop && (ff != 10));
			if (stop) idcomp[ff-1] = '\0';
			
			if (!strcmp(ident, idcomp)) {
				if (usedtaxa[i]) {
					fprintf(STDOUT, "\n\n\nUnable to proceed (multiple occurence of sequence '");
					fprintf(STDOUT, "%s' in tree)\n\n\n", ident);
#					if PARALLEL
						PP_Finalize();
#					endif
   					tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
				}
				usedtaxa[i] = TRUE;

				/* read edgelength !!!! (HAS) */
				if (**chpp == ':') {
					(void) sreadedgelength(&blen, chpp);
					tr->ebrnchp[i]->lengthext=blen;
					tr->ebrnchp[i]->lengthset=1;
					tr->ebrnchp[i]->kinp->lengthext=blen;
					tr->ebrnchp[i]->kinp->lengthset=1;
				}

				return tr->ebrnchp[i]->kinp;
			}
		}
		
		fprintf(STDOUT, "\n\n\nUnable to proceed (unknown sequence '%s' in tree)\n\n\n", ident);
#		if PARALLEL
			PP_Finalize();
#		endif
   		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}
	return NULL; /* never returned but without some compilers complain */
} /* internalnode */


/* make tree structure, the tree description may contain internal
    labels but no edge lengths */
void constructtree(Tree *tr, cvector strtree, int usebranch)
{
	char *chp;		/* char ptr to tree string */
	int   ninode;		/* number of internal nodes < Maxspc-3 */
	int   i;		/* counter */
	int   dvg;		/* degree vertex ? */
	int   numc;		/* char counter */
	Node *xp;		/* last read Node/Subtree */
	Node *np;		/* newly read Node/Subtree */

	ninode = 0;
	chp = strtree;
	usedtaxa = new_ivector(Maxspc);
	for (i = 0; i < Maxspc; i++) usedtaxa[i] = FALSE;

	xp = internalnode(tr, &chp, &ninode);
	xp->isop = xp;
	dvg = 1;
	while (*chp != ')') { /* look for closing bracket */
		if (*chp == '\0') {
			fprintf(STDOUT, "\n\n\nUnable to proceed (unexpected end of tree)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}
		dvg++;
		/* insert edges around node */
		np = internalnode(tr, &chp, &ninode);
		np->isop = xp->isop;
		xp->isop = np; 
		xp = np;
	}
	
	for (i = 0; i < Maxspc; i++)
		if (usedtaxa[i] == FALSE) {
			fprintf(STDOUT, "\n\n\nUnable to proceed (sequences missing in tree)\n\n\n");
#			if PARALLEL
				PP_Finalize();
#			endif
   			tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
		}
	
	/* closing bracket reached */
	if (dvg < 3) {
		fprintf(STDOUT, "\n\n\nUnable to proceed (no unrooted tree)\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
   		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}
	tr->rootp = xp;
	Numibrnch = ninode;
	Numbrnch = Numspc + ninode;

	chp++;
	if (*chp == ';' || *chp == '\0') {
		free_ivector(usedtaxa);
		return;
	}

	/* copy last internal label (max. 20 characters) */
	xp->label = new_cvector(21);
	(xp->label)[0] = *chp;
	(xp->label)[1] = '\0';		
	for (numc = 1; numc < 20; numc++) {
		chp++;
		if (*chp == ';' || *chp == '\0') {
			free_ivector(usedtaxa);
			return;
		} else {
			(xp->label)[numc] = *chp;
			(xp->label)[numc+1] = '\0';
		}
	}
	free_ivector(usedtaxa);	
	return;
} /* constructtree */


/* remove possible basal bifurcation */
void removebasalbif(cvector strtree)
{
	int n, c, brak, cutflag, h;
	
	/* check how many OTUs on basal level */
	n = 0;
	c = 0;
	brak = 0;
	do {
		if (strtree[n] == '(') brak++;
		if (strtree[n] == ')') brak--;
		
		if (strtree[n] == ',' && brak == 1) c++; /* number of commas in outer bracket */
		
		n++;
	} while (strtree[n] != '\0');
	
	/* if only 1 OTU inside outer bracket stop now */
	if (c == 0) {
		fprintf(STDOUT, "\n\n\nUnable to proceed (Only 1 OTU inside outer bracket in tree)\n\n\n");
#		if PARALLEL
			PP_Finalize();
#		endif
   		tp_exit(1, NULL, FALSE, __FILE__, __LINE__, exit_wait_optn);
	}	
	
	/* if only 2 OTUs inside outer bracket delete second pair of
	   brackets from the right to remove basal bifurcation */

	if (c == 1) {
			
		n = 0;
		brak = 0;
		cutflag = 0; /* not yet cutted */
		h = 0;
		do {
			if (strtree[n] == '(') brak++;
			if (strtree[n] == ')') brak--;
			
			if (brak == 2 && cutflag == 0) cutflag = 1; /* cutting */
			if (brak == 1 && cutflag == 1) {
				cutflag = 2; /* cutted */
				/* leave out internal label */
				do {
					h++;	
				} while (strtree[n+h] != ')' && strtree[n+h] != ',');

			}
			
			if (cutflag == 1) strtree[n] = strtree[n+1];
			if (cutflag == 2) strtree[n-1] = strtree[n+h];
		
			n++;
		} while (strtree[n] != '\0');
	}
} /* removebasalbif */


void makeusertree(FILE *itfp, int usebranchlen)
{
	cvector strtree;
	
	strtree = new_cvector(CHAR_PER_TREENODE*2*Maxspc); /* for treefile */ 
#if 0
fprintf(stdout, "XXX allocating %d bytes RAM (%d x 2 x %d)\n", CHAR_PER_TREENODE*2*Maxspc, CHAR_PER_TREENODE, Maxspc);
#endif
	getusertree(itfp, strtree, CHAR_PER_TREENODE*2*Maxspc, usebranchlen);
	removebasalbif(strtree);
	constructtree(Ctree, strtree, usebranchlen);
	free_cvector(strtree);
} /* makeusertree */


/******************************************************************************/
/* memory organisation for maximum likelihood tree                            */
/******************************************************************************/

/* initialise new tree */
Tree *new_tree(int maxspc, int numptrn, cmatrix seqconint)
{
	int n, i, maxibrnch;
	Tree *tr;
	Node *dp, *up;
	
	maxibrnch = maxspc - 3;
	heights = (Node **) calloc((size_t)(maxspc-2), sizeof(Node *));
	if (heights == NULL) maerror("heights in new_tree");
	tr = (Tree *) calloc((size_t)1, sizeof(Tree));
	if (tr == NULL) maerror("tr in new_tree");
	tr->ebrnchp = (Node **) calloc((size_t)maxspc, sizeof(Node *));
	if (tr->ebrnchp == NULL) maerror("ebrnchp in new_tree");
	tr->ibrnchp = (Node **) calloc((size_t)maxibrnch, sizeof(Node *));
	if (tr->ibrnchp == NULL) maerror("ibrnchp in new_tree");
	tr->condlkl = new_dmatrix(numcats, numptrn);	
	for (n = 0; n < maxspc; n++) {
		dp = (Node *) calloc((size_t) 1, sizeof(Node));
		if (dp == NULL) maerror("dp in new_tree");
		up = (Node *) calloc((size_t) 1, sizeof(Node));
		if (up == NULL) maerror("up in new_tree");
		dp->isop      = NULL;
		up->isop      = NULL;
		dp->kinp      = up;
		up->kinp      = dp;
		dp->descen    = TRUE;
		up->descen    = FALSE;
		dp->number    = n;
		up->number    = n;
		dp->length    = 0.0;
		up->length    = 0.0;
		dp->lengthc   = 0.0;
		up->lengthc   = 0.0;
		dp->lengthext = 0.0;
		up->lengthext = 0.0;
		dp->lengthset = FALSE;
		up->lengthset = FALSE;
		dp->varlen    = 0.0;
		up->varlen    = 0.0;
		dp->paths     = new_ivector(maxspc);
		up->paths     = dp->paths;
		for (i = 0; i < maxspc; i++) dp->paths[i] = 0;
		dp->paths[n]  = 1;
		dp->eprob     = seqconint[n];
		up->eprob     = NULL;
		dp->partials  = NULL;
		up->partials  = new_dcube(numcats, numptrn, tpmradix);
		tr->ebrnchp[n] = dp;
		up->label     = NULL;
		dp->label     = NULL;
	}
	for (n = 0; n < maxibrnch; n++) {
		dp = (Node *) calloc((size_t) 1, sizeof(Node));
		if (dp == NULL) maerror("dp in new_tree");
		up = (Node *) calloc((size_t) 1, sizeof(Node));
		if (up == NULL) maerror("up in new_tree");
		dp->isop      = NULL;
		up->isop      = NULL;
		dp->kinp      = up;
		up->kinp      = dp;
		dp->descen    = TRUE;
		up->descen    = FALSE;
		dp->number    = n;
		up->number    = n;
		dp->length    = 0.0;
		up->length    = 0.0;
		dp->lengthc   = 0.0;
		up->lengthc   = 0.0;
		dp->lengthext = 0.0;
		up->lengthext = 0.0;
		dp->lengthset = FALSE;
		up->lengthset = FALSE;
		dp->varlen    = 0.0;
		up->varlen = 0.0;
		dp->paths = new_ivector(maxspc);
		up->paths = dp->paths;
		for (i = 0; i < maxspc; i++) dp->paths[i] = 0;
		dp->eprob = NULL;
		up->eprob = NULL;
		dp->partials = new_dcube(numcats, numptrn, tpmradix);
		up->partials = new_dcube(numcats, numptrn, tpmradix);
		tr->ibrnchp[n] = dp;
		up->label = NULL;
		dp->label = NULL;
	}
	tr->rootp = NULL;
	
	/*
	 * reserve memory for lengths of the tree branches
	 * and for the distance matrix as a vector
	 * (needed for LS estimation of tree branch lengths)
	 */ 
	 
	Brnlength = new_dvector(2 * maxspc - 3); 
	Distanvec = new_dvector((maxspc * (maxspc - 1)) / 2);

	return tr;
} /* new_tree */


/* initialise quartet tree */
Tree *new_quartet(int numptrn, cmatrix seqconint)
{
	int n, i;
	Tree *tr;
	Node *dp, *up;

	heights = (Node **) calloc((size_t) 2, sizeof(Node *));
	if (heights == NULL) maerror("heights in new_quartet");
	/* reserve memory for tree */
	tr = (Tree *) calloc((size_t) 1, sizeof(Tree));
	if (tr == NULL) maerror("tr in new_quartet");
	tr->ebrnchp = (Node **) calloc((size_t) 4, sizeof(Node *));
	if (tr->ebrnchp == NULL) maerror("ebrnchp in new_quartet");
	tr->ibrnchp = (Node **) calloc((size_t) 1, sizeof(Node *));
	if (tr->ibrnchp == NULL) maerror("ibrnchp in new_quartet");
	tr->condlkl = new_dmatrix(numcats, numptrn);
	/* reserve memory for nodes */
	for (n = 0; n < 4; n++) {
		dp = (Node *) calloc((size_t) 1, sizeof(Node));
		if (dp == NULL) maerror("dp in new_quartet");
		up = (Node *) calloc((size_t) 1, sizeof(Node));
		if (up == NULL) maerror("dp in new_quartet");
		dp->isop = NULL;
		dp->kinp = up;
		up->kinp = dp;
		dp->descen = TRUE;
		up->descen = FALSE;
		dp->number = n;
		up->number = n;
		dp->length = 0.0;
		up->length = 0.0;
		dp->lengthc = 0.0;
		up->lengthc = 0.0;
		dp->lengthext = 0.0;
		up->lengthext = 0.0;
		dp->lengthset = FALSE;
		up->lengthset = FALSE;
		dp->varlen = 0.0;
		up->varlen = 0.0;
		dp->paths = new_ivector(4);
		up->paths = dp->paths;
		for (i = 0; i < 4; i++) dp->paths[i] = 0;
		dp->paths[n] = 1;
		dp->eprob = seqconint[n]; /* make quartet (0,1)-(2,3) as default */
		up->eprob = NULL;		
		dp->partials = NULL;
		up->partials = new_dcube(numcats, numptrn, tpmradix);
		tr->ebrnchp[n] = dp;
	}

	/* reserve memory for internal branch */	
	dp = (Node *) calloc((size_t) 1, sizeof(Node));
	if (dp == NULL) maerror("dp in new_quartet");	
	up = (Node *) calloc((size_t) 1, sizeof(Node));
	if (up == NULL) maerror("dp in new_quartet");	
	dp->isop = tr->ebrnchp[3]->kinp; /* connect internal branch */
	up->isop = tr->ebrnchp[0]->kinp;
	dp->kinp = up;
	up->kinp = dp;
	dp->descen = TRUE;
	up->descen = FALSE;
	dp->number = 0;
	up->number = 0;
	dp->length = 0.0;
	up->length = 0.0;
	dp->lengthc = 0.0;
	up->lengthc = 0.0;
	dp->lengthext = 0.0;
	up->lengthext = 0.0;
	dp->lengthset = FALSE;
	up->lengthset = FALSE;
	dp->varlen = 0.0;
	up->varlen = 0.0;
	dp->paths = new_ivector(4);
	up->paths = dp->paths;
	up->paths[0] = 0;
	up->paths[1] = 0;
	up->paths[2] = 1;
	up->paths[3] = 1;
	dp->eprob = NULL;
	up->eprob = NULL;	
	dp->partials = new_dcube(numcats, numptrn, tpmradix);
	up->partials = new_dcube(numcats, numptrn, tpmradix);	
	tr->ibrnchp[0] = dp;
	
	/* place root */
	tr->rootp = up;

	/* connect external branches */ 
	tr->ebrnchp[0]->kinp->isop = tr->ebrnchp[1]->kinp;
	tr->ebrnchp[1]->kinp->isop = tr->rootp;
	tr->ebrnchp[3]->kinp->isop = tr->ebrnchp[2]->kinp;
	tr->ebrnchp[2]->kinp->isop = tr->rootp->kinp;
	
	/*
	 * reserve memory for lengths of the five branches
	 * of a quartet and for the six possible distances
	 * (needed for LS estimation of branch lengths)
	 */
	Brnlength = new_dvector(NUMQBRNCH); 
	Distanvec = new_dvector(NUMQSPC*(NUMQSPC-1)/2);

	return tr;
} /* new_quartet */


/* free tree memory */
void free_tree(Tree *tr, int taxa)
{	
	int n;
	Node *dp, *up;

	free(heights);
	free_dmatrix(tr->condlkl);
	for (n = 0; n < taxa; n++) {
		dp = tr->ebrnchp[n];
		up = dp->kinp;
		free_ivector(dp->paths);		
		free_dcube(up->partials);		
		free(dp);
		free(up);
	}
	free(tr->ebrnchp);
	for (n = 0; n < (taxa-3); n++) {
		dp = tr->ibrnchp[n];
		up = dp->kinp;
		free_dcube(dp->partials);
		free_dcube(up->partials);
		free_ivector(dp->paths);
		if (dp->label != NULL) free_cvector(dp->label);
		if (up->label != NULL) free_cvector(up->label);
		free(dp);
		free(up);
	}
	free(tr->ibrnchp);
	free(tr);
	free_dvector(Brnlength); /* branch lengths (for LS estimation) */
	free_dvector(Distanvec); /* distances (for LS estimation) */
} /* free_tree */


/* make (a,b)-(c,d) quartet

	a ---+     +--- c
	     +-----+
	b ---+     +--- d

	species numbers range from 0 to Maxspc - 1  */

void make_quartet(int a, int b, int c, int d)
{
	/* place sequences */
	Ctree->ebrnchp[0]->eprob = Seqpat[a];
	Ctree->ebrnchp[1]->eprob = Seqpat[b];
	Ctree->ebrnchp[2]->eprob = Seqpat[c];
	Ctree->ebrnchp[3]->eprob = Seqpat[d];
	
	/* make distance vector */
	Distanvec[0] = Distanmat[b][a];
	Distanvec[1] = Distanmat[c][a];
	Distanvec[2] = Distanmat[c][b];
	Distanvec[3] = Distanmat[d][a];
	Distanvec[4] = Distanmat[d][b];
	Distanvec[5] = Distanmat[d][c];
} /* make_quartet */


/* write distance matrix as vector */
/* epe ??? (HAS) */
void changedistan(dmatrix distanmat, dvector distanvec, int numspc)
{
	int i, j, k;

	for (k = 0, i = 1; i < numspc; i++) {
		for (j = 0; j < i; j++, k++)
			distanvec[k] = distanmat[i][j];
	}
} /* changedistan */


/******************************************************************************/
/* computation of maximum likelihood tree                                     */
/******************************************************************************/


/* compute the likelihood for (a,b)-(c,d) quartet (+ inner length) */
double quartet_lklhd(int a, int b, int c, int d, double *iblen)
{
	/* reserve memory for quartet if necessary */
	if (mlmode != ML_QUART) { /* no quartet tree */
		if (Ctree != NULL)
			free_tree(Ctree, Numspc);
		Ctree = new_quartet(Numptrn, Seqpat);
		Numbrnch = NUMQBRNCH;
		Numibrnch = NUMQIBRNCH;
		Numspc = NUMQSPC;
		mlmode = ML_QUART;
	}
	
	/* make (a,b)-(c,d) quartet */
	make_quartet(a,b,c,d);

	clockmode = CLOCK_OFF; /* nonclocklike branch lengths */
	
	/* least square estimate for branch length */	
	lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);

	/* compute likelihood */
	Ctree->lklhd = optlkl(Ctree, clockmode);

	*iblen = Ctree->ibrnchp[0]->length;
	return Ctree->lklhd;
} /* quartet_lklhd */


/* compute the approximate likelihood for (a,b)-(c,d) quartet (+ inner length) */
double quartet_alklhd(int a, int b, int c, int d, double *iblen)
{
	/* reserve memory for quartet if necessary */
	if (mlmode != ML_QUART) { /* no quartet tree */
		if (Ctree != NULL)
			free_tree(Ctree, Numspc);
		Ctree = new_quartet(Numptrn, Seqpat);
		Numbrnch = NUMQBRNCH;
		Numibrnch = NUMQIBRNCH;
		Numspc = NUMQSPC;
		mlmode = ML_QUART; 
	}
	
	/* make (a,b)-(c,d) quartet */
	make_quartet(a,b,c,d);

	clockmode = CLOCK_OFF; /* nonclocklike branch lengths */
	
	/* least square estimate for branch length */	
	lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);

	/* compute likelihood */
	Ctree->lklhd = treelkl(Ctree, clockmode);

	*iblen = Ctree->ibrnchp[0]->length;
	return Ctree->lklhd;
} /* quartet_alklhd */

#if 0

/* compute the approximate likelihood for (a,b)-(c,d) quartet */
double quartet_ME(int a, int b, int c, int d, dmatrix distanmat)
{
	double me;

	me = distanmat[a][b] + distanmat[a][b]
	     + distanmat[a][c]
	     + distanmat[a][d]
	     + distanmat[b][c]
	     + distanmat[b][d]
	     + distanmat[c][d] + distanmat[c][d];

/* ??????
	me = distanmat[a][b] * distanmat[a][b]
	     + distanmat[a][c]
	     + distanmat[a][d]
	     + distanmat[b][c]
	     + distanmat[b][d]
	     + distanmat[c][d] * distanmat[c][d];
*/

	return (me / 4);
} /* quartet_ME */

#endif


/* read usertree from file to memory */
void readusertree(FILE *ifp, int usebranch)
{
	/* reserve memory for tree if necessary */
	if (mlmode != ML_TREE) { /* no tree */
		if (Ctree != NULL)
			free_tree(Ctree, Numspc);
		Ctree = new_tree(Maxspc, Numptrn, Seqpat);
		Numbrnch = 2*Maxspc-3;
		Numibrnch = Maxspc-3;
		Numspc = Maxspc;
		mlmode = ML_TREE;
	}

	/* read tree */
	makeusertree(ifp, usebranch);
} /* readusertree */


/* compute the likelihood of a usertree */
double usertree_lklhd(int usebranch, int parallel)
{
	/* be sure to have a usertree in memory and
	   to have pairwise distances computed */

	clockmode = CLOCK_OFF; /* nonclocklike branch lengths */

	if(usebranch) {
		/* set preset barnch lengths from usertree file */
		setextlength(Ctree, Numspc, Numibrnch, Brnlength, clockmode);
  
		/* compute likelihood */
		Ctree->lklhd = treelkl(Ctree, clockmode);
  
	} else {
		/* least square estimate for branch length */
		changedistan(Distanmat, Distanvec, Numspc);     
/*epe*/
#		if PARALLEL
		if (parallel)
			lslength_par(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);
		else
#		endif
			lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);


		/* compute and optimize likelihood */
		Ctree->lklhd = optlkl(Ctree, clockmode);
	}

	return Ctree->lklhd;
} /* usertree_lklhd */


/* compute the approximate likelihood of a usertree */
double usertree_alklhd(int usebranch, int parallel)
{
	/* be sure to have a usertree in memory and
	   to have pairwise distances computed */

	clockmode = CLOCK_OFF; /* nonclocklike branch lengths */

	if(usebranch) {
		/* set preset barnch lengths from usertree file */
		setextlength(Ctree, Numspc, Numibrnch, Brnlength, clockmode);

	} else {
		/* least square estimate for branch length */
		changedistan(Distanmat, Distanvec, Numspc);
/*epe*/
#		if PARALLEL
		if (parallel)
			lslength_par(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);
		else
#		endif
			lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);

	}

	/* compute likelihood */
	Ctree->lklhd = treelkl(Ctree, clockmode);

	return Ctree->lklhd;
} /* usertree_alklhd */


/* preparation for ML analysis */
void mlstart()
{
	/* number of states and code length */
	tpmradix = gettpmradix();

	/* declare variables */
	Eval = new_dvector(tpmradix);
	Evec = new_dmatrix(tpmradix,tpmradix);
	Ievc = new_dmatrix(tpmradix,tpmradix);
	iexp = new_dmatrix(tpmradix,tpmradix);
	Alias = new_ivector(Maxsite);

	/* process sequence information */
	evaluateseqs();
	bestrate = new_ivector(Numptrn);
	/* sitepostprob[k] = new_dvector(Numptrn);   posterior sitelhl */

	/* compute transition probability matrix */
	tranprobmat();

	/* non-zero rate categories */
	Rates = new_dvector(numcats);
	updaterates();
	ltprobr = new_dcube(numcats, tpmradix,tpmradix);

	/* compute distance matrix */
	Distanmat = new_dmatrix(Maxspc, Maxspc);
	initdistan();

	/* initialize tree pointer for quartet tree */
	mlmode = ML_QUART;
	Ctree = new_quartet(Numptrn, Seqpat);
	Numbrnch = NUMQBRNCH;
	Numibrnch = NUMQIBRNCH;
	Numspc = NUMQSPC;

	/* computing ML distances */
/*epe	computedistan() moved to inputandinit;*/

} /* mlstart */


/* recompute ml distances for quartet only */
void distupdate(int a, int b, int c, int d)
{
	/* update distance matrix */
	/* consider only entries relevant to quartet */
	Distanmat[a][b] = mldistance(a, b, Distanmat[a][b]);
	Distanmat[b][a] = Distanmat[a][b];
	Distanmat[a][c] = mldistance(a, c, Distanmat[a][c]);
	Distanmat[c][a] = Distanmat[a][c];
	Distanmat[a][d] = mldistance(a, d, Distanmat[a][d]);
	Distanmat[d][a] = Distanmat[a][d];
	Distanmat[b][c] = mldistance(b, c, Distanmat[b][c]);
	Distanmat[c][b] = Distanmat[b][c];
	Distanmat[b][d] = mldistance(b, d, Distanmat[b][d]);
	Distanmat[d][b] = Distanmat[b][d];
	Distanmat[c][d] = mldistance(c, d, Distanmat[c][d]);
	Distanmat[d][c] = Distanmat[c][d];
} /* distupdate */


/* cleanup after ML analysis */
void mlfinish()
{
	if (Ctree != NULL)
		free_tree(Ctree, Numspc);
	free_ivector(bestrate);
	/* free_ivector(sitepostprob);   posterior sitelhl */
	free_ivector(Alias);
	free_cmatrix(Seqpat);
	free_ivector(constpat);
	free_ivector(Weight);
	free_dmatrix(Distanmat);
	free_dvector(Eval);
	free_dmatrix(Evec);
	free_dmatrix(Ievc);
	free_dvector(Rates);
	free_dcube(ltprobr);
	free_dmatrix(iexp);
} /* mlfinish */


/******************************************************************************/
/* tree output                                                                */
/******************************************************************************/


#define MAXOVER    50
#define MAXLENG    30
#define MAXCOLUMN  80


/* print branch to outfp recursively. current node=*up */
void prbranch(Node *up, int depth, int m, int maxm,
	ivector umbrella, ivector column, FILE *outfp, int clockmode)
{
	int i, num, n, maxn, lim;
	Node *cp;
	char bch;

	if ((int)((clockmode ? up->lengthc : up->length) * Proportion) >= MAXOVER) {
		column[depth] = MAXLENG;
		bch = '+';
	} else {
		column[depth] = (int)((clockmode ? up->lengthc : up->length) * Proportion) + 3;
		bch = '-';
	}

	if (up->isop == NULL) { /* external branch */
		num = up->number + 1; /* offset */
		if (m == 1) umbrella[depth - 1] = TRUE;
		for (i = 0; i < depth; i++) {
			if (umbrella[i])
				fprintf(outfp, "%*c", column[i], ':');
			else
				fprintf(outfp, "%*c", column[i], ' ');
		}
		if (m == maxm)
			umbrella[depth - 1] = FALSE;
		for (i = 0, lim = column[depth] - 3; i < lim; i++)
			fputc(bch, outfp);
		fprintf(outfp, "-%d ", num);
				
		fputid(outfp, up->number);
		
		
		fputc('\n', outfp);
		fputc(' ', outfp);
		return;
	}

	num = up->number + 1 + Numspc; /* offset, internal branch */
	for (cp = up->isop, maxn = 0; cp != up; cp = cp->isop, maxn++)
		;
	for (cp = up->isop, n = 1; cp != up; cp = cp->isop, n++) {
		prbranch(cp->kinp, depth + 1, n, maxn, umbrella, column, outfp, clockmode);
		if (m == 1 && n == maxn / 2) umbrella[depth - 1] = TRUE;
		if (n != maxn) {
			for (i = 0; i < depth; i++) {
				if (umbrella[i])
					fprintf(outfp, "%*c", column[i], ':');
				else
					fprintf(outfp, "%*c", column[i], ' ');
			}
			if (n == maxn / 2) { /* internal branch */
				for (i = 0, lim = column[depth] - 3; i < lim; i++)
					fputc(bch, outfp);
				if (num < 10)
					fprintf(outfp, "--%d", num);
				else if (num < 100)
					fprintf(outfp, "-%2d", num);
				else
					fprintf(outfp, "%3d", num);
			} else {
				if (umbrella[depth])
					fprintf(outfp, "%*c", column[depth], ':');
				else
					fprintf(outfp, "%*c", column[depth], ' ');
			}
			fputc('\n', outfp);
			fputc(' ', outfp);
		}
		if (m == maxm) umbrella[depth - 1] = FALSE;
	}
	return;
} /* prbranch */


void getproportion(double *proportion, dvector distanvec, int numspc)
{
	int i, maxpair;
	double maxdis;
	
	maxpair = (numspc*(numspc-1))/2;

	maxdis = 0.0;
	for (i = 0; i < maxpair; i++) {
		if (distanvec[i] > maxdis) {
			maxdis = distanvec[i];
		}
	}
	*proportion = (double) MAXCOLUMN / (maxdis * 3.0);
	if (*proportion > 1.0) *proportion = 1.0;
} /* getproportion */


/* print tree to outfp recursively. current node=*up */
void prtopology(FILE *outfp, int clockmode)
{
	int n, maxn, depth;
	ivector umbrella;
	ivector column;
	Node *cp, *rp;
	
	getproportion(&Proportion, Distanvec, Numspc);

	umbrella = new_ivector(Numspc);
	column = new_ivector(Numspc);

	for (n = 0; n < Numspc; n++) {
		umbrella[n] = FALSE;
		column[n] = 3;
	}
	column[0] = 1;
	
	fputc(' ', outfp);

	/* original code: rp = Ctree->rootp */
	/* but we want to print the first group in the 
	   trichotomy as outgroup at the bottom! */
	rp = Ctree->rootp->isop;
	
	for (maxn = 1, cp = rp->isop; cp != rp; cp = cp->isop, maxn++)
		;
	depth = 1;
	n = 0;

	cp = rp;
	do {
		cp = cp->isop;
		n++;
		prbranch(cp->kinp, depth, n, maxn, umbrella, column, outfp, clockmode);
		if (cp != rp) fprintf(outfp, "%*c\n ", column[0], ':');
	} while (cp != rp);

	free_ivector(umbrella);
	free_ivector(column);
} /* prtopology */


/* print unrooted tree file with branch lengths */
void fputphylogeny(FILE *fp, int clockmode, int dofoldlines)
{
	Node *cp, *rp;
	int n;

	cp = rp = Ctree->rootp;
	putc('(', fp);
	n = 1;
	do {
		cp = cp->isop->kinp;
		if (cp->isop == NULL) { /* external node */
			if (dofoldlines && (n > 60)) {
				fprintf(fp, "\n");
				n = 2;
			}
			n += fputid(fp, cp->number);
			fprintf(fp, ":%.5f", ((clockmode ? cp->lengthc : cp->length))*0.01);
			n += 7;
			cp = cp->kinp;
		} else { /* internal node */
			if (cp->descen) {
				if (dofoldlines && (n > 60)) {
					fprintf(fp, "\n");
					n = 1;
				}
				putc('(', fp);
				n++;
			} else {
				putc(')', fp);
				n++;
				if (dofoldlines && (n > 60)) {
					fprintf(fp, "\n");
					n = 1;
				}
				/* internal label */
				if (cp->kinp->label != NULL) {
					fprintf(fp, "%s", cp->kinp->label);
					n += strlen(cp->kinp->label);
				}
				fprintf(fp, ":%.5f", ((clockmode ? cp->lengthc : cp->length))*0.01);
				n += 7;
			}
		}
		if (!cp->descen && !cp->isop->descen && cp != rp) {
			putc(',', fp); /* not last subtree */
			n++;
		}
	} while (cp != rp);
	fprintf(fp, ")");
	/* internal label */
	if (cp->label != NULL)
		fprintf(fp, "%s", cp->label);
	fprintf(fp, ";\n");
} /* fputphylogeny */


/* print tree statistics: branch length, optimization */
void resulttree(FILE *outfp, int usebranch, int clockmode)
{
	int n, ne, closeflag;
	Node *ep, *ip;
	double blen;
	
	closeflag = FALSE;
	
	if (clockmode) {
		fprintf(outfp, "\n         branch  length     nc/c");
		fprintf(outfp, "   branch  length     nc/c (= non-clock/clock)\n");
	} else {
		fprintf(outfp, "\n         branch  length     S.E.");
		fprintf(outfp, "   branch  length     S.E.\n");
	}
	for (n = 0; n < Numspc; n++) {
		ep = Ctree->ebrnchp[n];
		ne = ep->number;
		fputid10(outfp, ne);
		fputs("  ", outfp);
		fprintf(outfp, "%3d", ne + 1);
		blen = (clockmode ? ep->lengthc : ep->length);
		fprintf(outfp, "%9.5f", blen*0.01);
		if (blen < 5.0*MINARC || blen > 0.95*MAXARC) closeflag = TRUE;
		if (clockmode)
			fprintf(outfp, "%9.3f", (ep->length)/(ep->lengthc));
		else
			fprintf(outfp, "%9.5f", 0.01*sqrt(ep->kinp->varlen));	
		if (n < Numibrnch) {
			ip = Ctree->ibrnchp[n];
			fprintf(outfp, "%8d", n + 1 + Numspc);
			blen = (clockmode ? ip->lengthc : ip->length);
			fprintf(outfp, "%9.5f", blen*0.01);
			if (blen < 5.0*MINARC || blen > 0.95*MAXARC) closeflag = TRUE;
			if (clockmode)
				fprintf(outfp, "%9.3f", (ip->length)/(ip->lengthc));
			else
				fprintf(outfp, "%9.5f", 0.01*sqrt(ip->kinp->varlen));	
			fputc('\n', outfp);
		} else {
			if (n == Numspc - 3) {
				fputc('\n', outfp);
			} else if (n == Numspc - 2) {
				if (clockmode) {
					if (usebranch)  {
						fprintf(outfp, "     Branch lengths set by user (S.E.=0.0)!\n");
					} else {
						if (!Convergc) 
							fprintf(outfp, "     No convergence after %d iterations!\n", Numitc);
						else
							fprintf(outfp, "     %d iterations until convergence\n", Numitc);
					}
				} else {
					if (usebranch)  {
						fprintf(outfp, "     Branch lengths set by user (S.E.=0.0)!\n");
					} else {
						if (!Converg) 
							fprintf(outfp, "     No convergence after %d iterations!\n", Numit);
						else
							fprintf(outfp, "     %d iterations until convergence\n", Numit);				
					}
				}		
			} else if (n == Numspc - 1) {
				fprintf(outfp, "     log L: %.2f\n", (clockmode ? Ctree->lklhdc : Ctree->lklhd));
			} else {
				fputc('\n', outfp);
			}
		}
	}
	if(closeflag)
		fprintf(outfp, "\nWARNING --- at least one branch length is close to an internal boundary!\n");
} /* resulttree */


/******************************************************************************/
/* Neighbor-joining tree                                                      */
/******************************************************************************/


/* compute NJ tree and write to file */
void njtree(FILE *fp, int clockmode)
{
	/* reserve memory for tree if necessary */
	if (mlmode != NJ_TREE) { /* no tree */
		if (Ctree != NULL)
			free_tree(Ctree, Numspc);
		Ctree = new_tree(Maxspc, Numptrn, Seqpat);
		Numbrnch = 2*Maxspc-3;
		Numibrnch = Maxspc-3;
		Numspc = Maxspc;
		mlmode = NJ_TREE;
	}

	/* construct NJ tree from distance matrix */
	njdistantree(Ctree);
	
	fputphylogeny(fp, clockmode, FALSE);  /* FALSE = no <NL> needed */
} /* njtree */


/* construct  NJ tree from distance matrix */
void njdistantree(Tree *tr)
{
	int i, j, otui=0, otuj=0, otuk, nsp2, cinode, step, restsp, k;
	double dij, bix, bjx, bkx, sij, smax, dnsp2;
	dvector r;
	dmatrix distan;
	Node **psotu, *cp, *ip, *jp, *kp;

	distan = new_dmatrix(Maxspc,Maxspc);
	for (i = 0; i < Maxspc; i++)
		for (j = 0; j < Maxspc; j++)
			distan[i][j] = Distanmat[i][j];

	nsp2 = Maxspc - 2;
	dnsp2 = 1.0 / nsp2;
	
	r = new_dvector(Maxspc);
	
	psotu = (Node **) calloc((size_t)Maxspc, sizeof(Node *));
	if (psotu == NULL) maerror("psotu in njdistantree");

	/* external branches are start OTUs */
	for (i = 0; i < Maxspc; i++)
		psotu[i] = tr->ebrnchp[i]->kinp;
	
	restsp = Maxspc;
	cinode = 0; /* counter for internal nodes */
	
	for (step = 0; restsp > 3; step++) { /* NJ clustering steps */
	
		for (i = 0; i < Maxspc; i++) {
			if (psotu[i] != NULL) {
				for (j = 0, r[i] = 0.0; j < Maxspc; j++)
					if (psotu[j] != NULL)
						r[i] += distan[i][j];
			}
		}
		
		smax = -1.0;
		for (i = 0; i < Maxspc-1; i++) {
			if (psotu[i] != NULL) {
				
				for (j = i+1; j < Maxspc; j++) {
					if (psotu[j] != NULL)
					{
						sij = ( r[i] + r[j] ) * dnsp2 - distan[i][j];
			
						if (sij > smax) {
							smax = sij;
							otui = i;
							otuj = j;
						}
					}
				}
			}
		}

		/* new pair: otui and otuj */

		dij = distan[otui][otuj];
		bix = (dij + r[otui]/nsp2 - r[otuj]/nsp2) * 0.5;
		bjx = dij - bix;
		
		cp = tr->ibrnchp[cinode];
		
		ip = psotu[otui];
		jp = psotu[otuj];
		cp->isop = ip;
		ip->isop = jp;
		jp->isop = cp;
		ip->length = bix;
		jp->length = bjx;
		ip->kinp->length = ip->length;
		jp->kinp->length = jp->length;
		
		cp = cp->kinp;
		
		for (k = 0; k < Maxspc; k++)
		{
			if (psotu[k] != NULL && k != otui && k != otuj)
			{
				dij = (distan[otui][k] + distan[otuj][k] - distan[otui][otuj]) * 0.5;
				distan[otui][k] = dij;
				distan[k][otui] = dij;
			}
		}
		distan[otui][otui] = 0.0;

		psotu[otui] = cp;
		psotu[otuj] = NULL;
		
		cinode++;
		
		restsp--;
		nsp2--;
		dnsp2 = 1.0 / nsp2;
	}
 
	otui = otuj = otuk = -1;
	for (i = 0; i < Maxspc; i++)
	{
		if (psotu[i] != NULL) {
			if (otui == -1) otui = i;
			else if (otuj == -1) otuj = i;
			else otuk = i;
		}
	}
	bix = (distan[otui][otuj] + distan[otui][otuk] - distan[otuj][otuk]) * 0.5;
	bjx = distan[otui][otuj] - bix;
	bkx = distan[otui][otuk] - bix;
	ip = psotu[otui];
	jp = psotu[otuj];
	kp = psotu[otuk];
	ip->isop = jp;
	jp->isop = kp;
	kp->isop = ip;
	ip->length = bix;
	jp->length = bjx;
	kp->length = bkx;
	ip->kinp->length = ip->length;
	jp->kinp->length = jp->length;
	kp->kinp->length = kp->length;

	tr->rootp = kp;

	free_dvector(r);
	free_dmatrix(distan);
	free((Node *) psotu);
} /* njdistantree */

/******************************************************************************/
/* find best assignment of rate categories                                    */
/******************************************************************************/

/* find best assignment of rate categories */
#if 0
void findbestratecombination(Tree *ctree,  /* Ctree */
                             cmatrix Seqpat, 
                             dvector Freqtpm, 
                             double fracinv,
                             ivector constpat, 
                             ivector bestrate,
                             dvector sitepostprob,
                             int bestratefound,
                             int ncats)    /* numcats */
#endif
void findbestratecombination()
{
	int k, u;
	double bestvalue, fv2;
	/* double lklsum;   posterior sitelhl */
	dvector catprob;
	dmatrix cdl;
	/* dvector sitepostprob;   posterior sitelhl */

	/* cdl = ctree->condlkl; */
	cdl = Ctree->condlkl;


	/* catprob = new_dvector(ncats+1); */
	catprob = new_dvector(numcats+1);
	/* fv2 = (1.0-fracinv)/(double) ncats; */
	fv2 = (1.0-fracinv)/(double) numcats;

	for (k = 0; k < Numptrn; k++) {

		/* zero rate */
		if (constpat[k] == TRUE) {
			catprob[0] = fracinv*Freqtpm[(int) Seqpat[0][k]];
			/* lklsum = catprob[0];   posterior sitelhl */
		} else {
			catprob[0] = 0.0;
			/* lklsum = 0.0;   posterior sitelhl */
		}

		/* non-zero-rates */
		/* for (u = 1; u < ncats+1; u++) { */
		for (u = 1; u < numcats+1; u++) {
			catprob[u] = fv2*cdl[u-1][k];
			/* lklsum += catprob[u];   posterior sitelhl */
		}

		/* find best */
		bestvalue = catprob[0];
		bestrate[k] = 0;
		/* for (u = 1; u < ncats+1; u++) */
		for (u = 1; u < numcats+1; u++)
			if (catprob[u] >= bestvalue) {
				bestvalue = catprob[u];
				bestrate[k] = u;
			}
		/* sitepostprob[k] = bestvalue / lklsum;   posterior sitelhl */
	} /* for all column patterns */

	free_dvector(catprob);
	bestratefound = 1;
} /* findbestratecombination */


/* print best assignment of rate categories */
void printbestratecombination(FILE *fp)
{
	int s, k;

#ifndef USE_WINDOWS
	for (s = 0; s < Maxsite; s++) {
#else
	for (s = 0; s < alimaxsite; s++) {
#endif
		k = Alias[s];
		fprintf(fp, "%2d", bestrate[k]);
		if ((s+1) % 30 == 0)
			fprintf(fp, "\n");
		else if ((s+1) % 10 == 0)
			fprintf(fp, "  ");
	}
	if (s % 70 != 0)
		fprintf(fp, "\n");
} /* printbestratecombination */




#if 0
#  define MLUNIFORMRATE 0
#  define MLGAMMARATE   1
#  define MLTWORATE     2
#  define MLMIXEDRATE   3
#endif
void printbestratecombinationtofile(FILE *fp, int rhettype)
{
	int s;
	char name[11]; 

	switch(rhettype) {
		case GAMMARATE:
			sprintf(name, "%drates", numcats);
			break;;
       		case MIXEDRATE:
			sprintf(name, "1inv+%d", numcats);
			break;;
       		case TWORATE:
			sprintf(name, "1inv+1var");
			break;;
		default: /* UNIFORMRATE */
			sprintf(name, "uniform");
	}
#ifndef USE_WINDOWS
	fprintf(fp, " 1 %d %d \n", Maxsite, numcats);
	fprintf(fp, "%-10s", name);
	for (s = 0; s < Maxsite; s++) {
#else
	fprintf(fp, " 1 %d %d \n", alimaxsite, numcats);
	fprintf(fp, "%-10s", name);
	for (s = 0; s < alimaxsite; s++) {
#endif
		if (rhettype != UNIFORMRATE) {
			if(bestrate[Alias[s]] != 0)
				fprintf(fp, " %.5f", Rates[bestrate[Alias[s]] - 1]);
			else
				fprintf(fp, " %.5f", 0.0);
		} else {
			fprintf(fp, " 1.0");
		}
	}
	fprintf(fp, "\n");

/* 
#ifndef USE_WINDOWS
	fprintf(fp, " 1 %d %d \n", Maxsite, numcats);
	fprintf(fp, "%-10spl", name);
	for (s = 0; s < Maxsite; s++) {
#else
	fprintf(fp, " 1 %d %d \n", alimaxsite, numcats);
	fprintf(fp, "%-10spl", name);
	for (s = 0; s < alimaxsite; s++) {
#endif
		if (rhettype != UNIFORMRATE)
			fprintf(fp, " %.5f", sitepostprob[Alias[s]]);
		else
			fprintf(fp, " 1.0");
	}
	fprintf(fp, "\n");
posterior sitelhl */

} /* printbestratecombination */
#if 0
#  undef MLUNIFORMRATE
#  undef MLGAMMARATE
#  undef MLTWORATE
#  undef MLMIXEDRATE
#endif


/******************************************************************************/
/* computation of clocklike branch lengths                                    */
/******************************************************************************/

/* checks wether e is a valid edge specification */
int checkedge(int e)
{
	/* there are Numspc external branches:
	     0 - Numspc-1
	   there are Numibrnch internal branches:
	     Numspc - Numspc+Numibrnch-1
	*/
	
	if (e < 0) return FALSE;
	if (e < Numspc+Numibrnch) return TRUE;
	else return FALSE;  
} /* checkedge */

/* print topology of subtree */
void fputsubstree(FILE *fp, Node *ip, int dofoldlines)
{
	Node *cp;
	
	if (ip->isop == NULL) { /* terminal nodes */
		numtc += fputid(fp, ip->number);
	} else {	
		cp = ip;
		fprintf(fp, "(");
		numtc += 1;
		do {		
			cp = cp->isop->kinp;
			if (cp->isop == NULL) { /* external node */
				numtc += fputid(fp, cp->number);
				fprintf(fp, ":%.5f", (cp->lengthc)*0.01);
				numtc += 7;
				cp = cp->kinp;
			} else { /* internal node */
				if (cp->height > 0.0) {
					fprintf(fp, "(");
					numtc += 1;
				} else if (cp->height < 0.0) {
					fprintf(fp, ")");
					numtc += 1;
					if (dofoldlines && (numtc > 60)) {
						fprintf(fp, "\n");
						numtc = 1;
					}
					/* internal label */
					if (cp->kinp->label != NULL) {
						fprintf(fp, "%s", cp->kinp->label);
						numtc += strlen(cp->kinp->label);
					}
					if (dofoldlines && (numtc > 60)) {
						fprintf(fp, "\n");
						numtc = 1;
					}
					fprintf(fp, ":%.5f", (cp->lengthc)*0.01);
					numtc += 6;
					if (dofoldlines && (numtc > 60)) {
						fprintf(fp, "\n");
						numtc = 1;
					}
				}
			}
			if (cp->height <= 0.0 && cp->isop->height <= 0.0 &&
				cp->isop != ip) {
				putc(',', fp); /* not last subtree */
				numtc += 1;
				if (dofoldlines && (numtc > 60)) {
					fprintf(fp, "\n");
					numtc = 1;
				}
			}
		} while (cp->isop != ip);
		fprintf(fp, ")");
		numtc += 1;
	}
	if (dofoldlines && (numtc > 60)) {
		fprintf(fp, "\n");
		numtc = 1;
	}

} /* fputsubstree */

/* print rooted tree file  */
void fputrooted(FILE *fp, int e, int dofoldlines)
{
	Node *rootbr;
	
	/* to be called only after clocklike branch
	   lengths have been computed */
	 
	 /* pointer to root branch */
	if (e < Numspc) rootbr = Ctree->ebrnchp[e];
	else rootbr = Ctree->ibrnchp[e - Numspc];

	fprintf(fp, "(");
	numtc = 2;
	fputsubstree(fp, rootbr, dofoldlines);
	/* internal label */
	if (rootbr->label != NULL) {
		fprintf(fp, "%s", rootbr->label);
		numtc += strlen(rootbr->label);
	}
	if (dofoldlines && (numtc > 60)) {
		fprintf(fp, "\n");
		numtc = 1;
	}
	fprintf(fp, ":%.5f,", (hroot - rootbr->height)*0.01);
	numtc += 7;
	if (dofoldlines && (numtc > 60)) {
		fprintf(fp, "\n");
		numtc = 1;
	}
	fputsubstree(fp, rootbr->kinp, dofoldlines);
	/* internal label */
	if (rootbr->kinp->label != NULL) {
		fprintf(fp, "%s", rootbr->kinp->label);
		numtc += strlen(rootbr->kinp->label);
	}
	if (dofoldlines && (numtc > 60)) {
		fprintf(fp, "\n");
		numtc = 1;
	}
	fprintf(fp, ":%.5f);\n", (hroot - rootbr->kinp->height)*0.01);
} /* fputrooted */

/* finds heights in subtree: recursively collect branches in a path from root to leaves */
void findheights(Node *ip) /* TODO: heights[Numhts], Numhts++ */
{
	Node *cp, *rp;
	
	if (ip->isop != NULL) { /* forget terminal nodes */
		
		cp = ip;
		
		/* initialise node  */
		cp->height = 1.0; /* up */
		rp = cp;
		while (rp->isop != cp) {
			rp = rp->isop;
			rp->height = -1.0; /* down */
		}

		do {		
			cp = cp->isop->kinp;
			if (cp->isop == NULL) { /* external node */
				cp = cp->kinp;
			} else { /* internal node */
				if (cp->height == 0.0) { /* node not yet visited */
					cp->height = 1.0; /* up */
					rp = cp;
					while (rp->isop != cp) {
						rp = rp->isop;
						rp->height = -1.0; /* down */
					}
				} else if (cp->kinp->height == 1.0) {
					/* cp->kinp is next height pointer  */
					heights[Numhts] = cp->kinp;
					Numhts++;
				}
			}
		} while (cp->isop != ip);
		/* ip is last height pointer */
		heights[Numhts] = ip;
		Numhts++;
	}	
} /* findheights */


/* initialise clocklike branch lengths (with root on edge e) */
/* TODO: deglobalize hroot,... */
void initclock(int e)
{
	int n, h, count;
	Node *cp, *rp;
	double sum;	/* height sum */
	double minh;	/* minimal height */
	double aveh;	/* average height */
	double len;

	/* be sure to have a Ctree in memory and
	   to have pairwise distances computed */

	clockmode = CLOCK_ON; /* clocklike branch lengths */

	/* least square estimate for branch length */
	changedistan(Distanmat, Distanvec, Numspc); /* epe ??? (HAS) */
	lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);

	/* set pointer to root branch */
	if (e < Numspc) rootbr = Ctree->ebrnchp[e]; /* root in ext. branch */
	else rootbr = Ctree->ibrnchp[e - Numspc];   /* root in int. branch */
	   
	/* clear all heights */
	for (n = 0; n < Numspc; n++) {
		Ctree->ebrnchp[n]->height = 0.0;
		Ctree->ebrnchp[n]->kinp->height = 0.0;
		Ctree->ebrnchp[n]->varheight = 0.0;
		Ctree->ebrnchp[n]->kinp->varheight = 0.0;
		if (n < Numibrnch) {
			Ctree->ibrnchp[n]->height = 0.0;
			Ctree->ibrnchp[n]->kinp->height = 0.0;
			Ctree->ibrnchp[n]->varheight = 0.0;
			Ctree->ibrnchp[n]->kinp->varheight = 0.0;
		}
	}
	
	/* collect pointers to height nodes */
	Numhts = 0;
	findheights(rootbr); /* one side */
	findheights(rootbr->kinp); /* other side */

	/* assign preliminary approximate heights and
	   corresponding branch lengths */ 
	for (h = 0; h < Numhts; h++) {
		
		cp = rp = heights[h];
		sum = 0;
		count = 0;
		minh = 0.0;
		while (rp->isop != cp) {
			count++;
			rp = rp->isop;
			sum += rp->lengthc + rp->kinp->height;
			if (rp->kinp->height > minh) minh = rp->kinp->height;
		}
		aveh = sum / (double) count;
		if (aveh < minh + MINARC) aveh = minh + MINARC;
		cp->height = aveh;
		rp = cp;
		while (rp->isop != cp) { /* all descending branches */
			rp = rp->isop;
			len = aveh - rp->kinp->height;
			rp->kinp->lengthc = len;
			rp->lengthc = len;
		} 
		
	} /* for all heights */

	if (rootbr->height > rootbr->kinp->height) minh = rootbr->height;
	else minh = rootbr->kinp->height;
	aveh = 0.5*(rootbr->lengthc + rootbr->height + rootbr->kinp->height);
	if (aveh < minh + MINARC) aveh = minh + MINARC;	
	hroot = aveh;
	maxhroot = RMHROOT*hroot; /* maximal possible hroot */
	len = (hroot - rootbr->height) + (hroot - rootbr->kinp->height);
	rootbr->lengthc = len;
	rootbr->kinp->lengthc = len;
} /* initclock */


#if 0
/* initialise clocklike branch lengths (with root on edge e) */
/* TODO: deglobalize hroot,... */
void initstar()
{
	int n, h, count;
	Node *cp, *rp;
	double sum;	/* height sum */
	double minh;	/* minimal height */
	double aveh;	/* average height */
	double len;

	/* be sure to have a Ctree in memory and
	   to have pairwise distances computed */

	clockmode = CLOCK_ON; /* clocklike branch lengths */

	/* least square estimate for branch length */
	changedistan(Distanmat, Distanvec, Numspc); /* epe ??? (HAS) */
	lslength(Ctree, Distanvec, Numspc, Numibrnch, Brnlength, clockmode);

	/* set pointer to root branch */
	if (e < Numspc) rootbr = Ctree->ebrnchp[e]; /* root in ext. branch */
	else rootbr = Ctree->ibrnchp[e - Numspc];   /* root in int. branch */
	   
	/* clear all heights */
	for (n = 0; n < Numspc; n++) {
		Ctree->ebrnchp[n]->height = 0.0;
		Ctree->ebrnchp[n]->kinp->height = 0.0;
		Ctree->ebrnchp[n]->varheight = 0.0;
		Ctree->ebrnchp[n]->kinp->varheight = 0.0;
		if (n < Numibrnch) {
			Ctree->ibrnchp[n]->height = 0.0;
			Ctree->ibrnchp[n]->kinp->height = 0.0;
			Ctree->ibrnchp[n]->varheight = 0.0;
			Ctree->ibrnchp[n]->kinp->varheight = 0.0;
		}
	}
	
	/* collect pointers to height nodes */
	Numhts = 0;
	findheights(rootbr); /* one side */
	findheights(rootbr->kinp); /* other side */

	/* assign preliminary approximate heights and
	   corresponding branch lengths */ 
	for (h = 0; h < Numhts; h++) {
		
		cp = rp = heights[h];
		sum = 0;
		count = 0;
		minh = 0.0;
		while (rp->isop != cp) {
			count++;
			rp = rp->isop;
			sum += rp->lengthc + rp->kinp->height;
			if (rp->kinp->height > minh) minh = rp->kinp->height;
		}
		aveh = sum / (double) count;
		if (aveh < minh + MINARC) aveh = minh + MINARC;
		cp->height = aveh;
		rp = cp;
		while (rp->isop != cp) { /* all descending branches */
			rp = rp->isop;
			len = aveh - rp->kinp->height;
			rp->kinp->lengthc = len;
			rp->lengthc = len;
		} 
		
	} /* for all heights */

	if (rootbr->height > rootbr->kinp->height) minh = rootbr->height;
	else minh = rootbr->kinp->height;
	aveh = 0.5*(rootbr->lengthc + rootbr->height + rootbr->kinp->height);
	if (aveh < minh + MINARC) aveh = minh + MINARC;	
	hroot = aveh;
	maxhroot = RMHROOT*hroot; /* maximal possible hroot */
	len = (hroot - rootbr->height) + (hroot - rootbr->kinp->height);
	rootbr->lengthc = len;
	rootbr->kinp->lengthc = len;
} /* initstar */
#endif


/* approximate likelihood under the constraining assumption of
   clocklike branch lengths (with root on edge e) */
double clock_alklhd(int e)
{
	initclock(e);
	Ctree->lklhdc = treelkl(Ctree, CLOCK_ON);

	return Ctree->lklhdc;	
} /* clock_alklhd */


/* log-likelihood given height ht at node pointed to by chep (clock) */
/* global chep (= current height edge ptr) has to be set in advance (clock) */
double heightlkl(double ht)
{
	Node *rp;
	double len;	
	
	/* adjust branch lengths */
	chep->height = ht;
	/* descendent branches */
	rp = chep;
	while (rp->isop != chep) {
		rp = rp->isop;
		len = chep->height - rp->kinp->height;
		rp->kinp->lengthc = len;
		rp->lengthc = len;
	}
	/* upward branch */
	if (chep == rootbr || chep->kinp == rootbr) {
		len = (hroot - chep->height) + (hroot - chep->kinp->height);
		chep->lengthc = len;
		chep->kinp->lengthc = len;
	} else {
		rp = chep->kinp;
		while (rp->isop->height <= 0.0)
			rp = rp->isop;
		chep->lengthc = rp->isop->height - chep->height;
		chep->kinp->lengthc = rp->isop->height - chep->height;
	}

	/* compute likelihood */
	Ctree->lklhdc = treelkl(Ctree, CLOCK_ON);

	return -(Ctree->lklhdc); /* we use a minimizing procedure */
} /* heightlkl */


/* optimize current height (chep = current height edge ptr) */
/* TODO: deglobalize hroot */
void optheight(Node *chep)
{
	double he;	/* current height (of edge e =: he) */
	double fx = 0.0;
	double f2x = 0.0;
	double minh;	/* minimum height */
	double maxh;	/* maximum height */
	double len;	/* infered branch length */
	Node *rp;

	/* current height */
	he = chep->height;
	
	/* find minimum (= maximum, since all values are multiplied by -1) */
	minh = 0.0;
	rp = chep;
	while (rp->isop != chep) {
		rp = rp->isop;
		if (rp->kinp->height > minh)
			minh = rp->kinp->height;
	}
	minh += MINARC;
	
	/* maximum */
	if (chep == rootbr || chep->kinp == rootbr) {
		maxh = hroot;
	} else {
		rp = chep->kinp;
		while (rp->isop->height <= 0.0)
			rp = rp->isop;
		maxh = rp->isop->height;
	}
	maxh -= MINARC;

	/* check borders for height */
	if (he < minh) he = minh;
	if (he > maxh) he = maxh;

	/* optimization */
        if (!(he == minh && he == maxh)) {
		/* global variable chep has to be set in advance for heightlkl */
		he = onedimenmin(minh, he, maxh, heightlkl, EPSILON_HEIGHTS, &fx, &f2x);
	}
	
	/* variance of height */
	f2x = fabs(f2x);
	if (1.0/(maxhroot*maxhroot) < f2x)
		chep->varheight = 1.0/f2x;
	else
		chep->varheight = maxhroot*maxhroot;
	
	/* adjust branch lengths */
	chep->height = he;
	/* descendent branches */
	rp = chep;
	while (rp->isop != chep) {
		rp = rp->isop;
		len = chep->height - rp->kinp->height;
		rp->kinp->lengthc = len;
		rp->lengthc = len;
	}
	/* upward branch */
	if (chep == rootbr || chep->kinp == rootbr) {
		len = (hroot - chep->height) + (hroot - chep->kinp->height);
		chep->lengthc = len;
		chep->kinp->lengthc = len;
	} else {
		rp = chep->kinp;
		while (rp->isop->height <= 0.0)
			rp = rp->isop;
		chep->lengthc = rp->isop->height - chep->height;
		chep->kinp->lengthc = rp->isop->height - chep->height;
	}
} /* optheight */


/* log-likelihood given height ht at root */
double rheightlkl(double ht)
{
	double len;	
	
	/* adjust branch lengths */
	hroot = ht;
	len = (hroot - rootbr->height) + (hroot - rootbr->kinp->height);
	rootbr->lengthc = len;
	rootbr->kinp->lengthc = len;

	/* compute likelihood */
	Ctree->lklhdc = treelkl(Ctree, CLOCK_ON);

	return -(Ctree->lklhdc); /* we use a minimizing procedure */
} /* rheightlkl */


/* optimize height of root */
void optrheight(void)
{
	double he, fx, f2x, minh, len;

	/* current height */
	he = hroot;
	
	/* find minimum */
	if (rootbr->height > rootbr->kinp->height)
		minh = rootbr->height;
	else
		minh = rootbr->kinp->height;

	minh += MINARC;
		
	/* check borders for height */
	if (he < minh) he = minh;
	if (he > maxhroot) he = maxhroot;

	/* optimization */
	he = onedimenmin(minh, he, maxhroot, rheightlkl, EPSILON_HEIGHTS, &fx, &f2x);
	
	/* variance of height of root */
	f2x = fabs(f2x);
	if (1.0/(maxhroot*maxhroot) < f2x)
		varhroot = 1.0/f2x;
	else
		varhroot = maxhroot*maxhroot;

	/* adjust branch lengths */
	hroot = he;
	len = (hroot - rootbr->height) + (hroot - rootbr->kinp->height);
	rootbr->lengthc = len;
	rootbr->kinp->lengthc = len;
} /* optrheight */


/* exact likelihood under the constraining assumption of
   clocklike branch lengths (with root on edge e) */
double clock_lklhd(int e)
{
	int h, nconv;
	double old;
	
	Numitc = 0;
	Convergc = FALSE;
	
	initclock(e);
	
	do {
	
		Numitc++;
		nconv = 0;

		/* optimize height of root */
		old = hroot;
		optrheight();
		if (fabs(old - hroot) < EPSILON_HEIGHTS) nconv++;

		/* optimize height of nodes */
		for (h = Numhts-1; h >= 0; h--) {
		
			/* pointer chep to current height node */
			chep = heights[h];
			
			/* store old value */
			old = chep->height;

			/* find better height */
			optheight(chep);
			
			/* converged ? */
			if (fabs(old - chep->height) < EPSILON_HEIGHTS) nconv++;
		}

		if (nconv == Numhts+1) Convergc = TRUE;
			
	} while (Numitc < MAXIT && !Convergc);
		
	/* compute final likelihood */
	Ctree->lklhdc = treelkl(Ctree, CLOCK_ON);

	return Ctree->lklhdc;
} /* clock_lklhd */


#if 0
/* exact likelihood under the constraining assumption of
   clocklike branch lengths on a star-tree (with root in the middle) */
double star_lklhd()
{
	/* int h, nconv; */
	/* double old; */
	
	Numitc = 0;
	Convergc = FALSE;
	
	initclock(e);
	
	do {
	
		Numitc++;
		nconv = 0;

		/* optimize height of root */
		old = hroot;
		optrheight();
		if (fabs(old - hroot) < EPSILON_HEIGHTS) nconv++;

		/* optimize height of nodes */
		for (h = Numhts-1; h >= 0; h--) {
		
			/* pointer chep to current height node */
			chep = heights[h];
			
			/* store old value */
			old = chep->height;

			/* find better height */
			optheight(chep);
			
			/* converged ? */
			if (fabs(old - chep->height) < EPSILON_HEIGHTS) nconv++;
		}

		if (nconv == Numhts+1) Convergc = TRUE;
			
	} while (Numitc < MAXIT && !Convergc);
		
	/* compute final likelihood */
	Ctree->lklhdc = treelkl(Ctree, CLOCK_ON);

	return Ctree->lklhdc;
} /* star_lklhd */
#endif


/* find out the edge containing the root */
int findrootedge()
{
	int e, ebest;
	double logbest, logtest;
	
	/* compute the likelihood for all edges and take the edge with
	   best likelihood (using approximate ML) */

	ebest = 0;
	logbest = clock_alklhd(0);
	numbestroot = 1;
	for (e = 1; e < Numspc+Numibrnch; e++) {
		logtest = clock_alklhd(e);
		if (logtest > logbest) {
			ebest = e;
			logbest = logtest;
			numbestroot = 1;
		} else if (logtest == logbest) {
			numbestroot++;
		}
	}

	return ebest;
} /* findrootedge */


/* show heights and corresponding standard errors */
void resultheights(FILE *fp)
{
	int h, num;
	Node *cp;
	
	fprintf(fp, " height    S.E.    of node common to branches\n");
	for (h = 0; h < Numhts; h++) {
		fprintf(fp, "%.5f  %.5f    ", (heights[h]->height)*0.01,
			sqrt(heights[h]->varheight)*0.01);
		cp = heights[h];
		do {
			num = (cp->number) + 1;
			if (cp->kinp->isop != NULL) num += Numspc; /* internal branch */			
			fprintf(fp, "%d  ", num);
			cp = cp->isop;
		} while (cp != heights[h]);	
		fprintf(fp, "\n");
		
	}
	fprintf(fp, "%.5f  %.5f   of root at branch %d\n",
		hroot*0.01, sqrt(varhroot)*0.01, locroot+1);
} /* resultheights */

