/* 
 derived from file dnapars.c of PHYLIP version 3.52c with the following copyright:

 (c) Copyright 1980-2008. University of Washington. All rights reserved. Permission is 
 granted to reproduce, perform, and modify these programs and documentation files. 
 Permission is granted to distribute or provide access to these programs provided 
 that this copyright notice is not removed, the programs are not integrated with or 
 called by any product or service that generates revenue, and that your distribution 
 of these documentation files and programs are free. Any modified versions of these 
 materials that are distributed or accessible shall indicate that they are based on 
 these programs. Institutions of higher education are granted permission to 
 distribute this material to their students and staff for a fee to recover 
 distribution costs. Permission requests for any other distribution of these 
 program should be directed to  license (at) u.washington.edu . 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <limits.h>
#include <setjmp.h>
static jmp_buf lngjmp_env;

#define Malloc(x) mymalloc(x)

#define Void       void      /* Void f() = procedure */
#define Char        char      /* Characters (not bytes) */
#define Static     static     /* Private global funcs and vars */
#define Local      static     /* Nested functions */

typedef unsigned char boolean;

# define true    1
# define false   0

static void    *mymalloc(size_t x);

extern int tree_build_interrupted;

int nmlngth;                /* max number of characters in species name    */
#define maxuser         2   /* maximum number of user-defined trees */


typedef short *steptr;
typedef short *stepshortptr;
typedef enum {
  A, C, G, U, O
} bases;
typedef short *baseptr;
/* nodes will form a binary tree */

typedef struct node {           /* describes a tip species or an ancestor */
  struct node *next, *back;     /* pointers to nodes                      */
  short index;                   /* number of the node                     */
  boolean tip, bottom;          /* present species are tips of tree       */
  baseptr base;                 /* the sequence                           */
  stepshortptr numsteps;        /* bookkeeps steps                        */
  short xcoord, ycoord, ymin;    /* used by printree                       */
  short ymax;
} node;

typedef node **pointptr;
typedef short longer[6];
typedef char **sequence;


Static node *root, *p;
Static char *infile;
Static short spp, nonodes, chars, endsite, col, 
i, j;
/* spp = number of species
   nonodes = number of nodes in tree
   chars = number of sites in actual sequences
   outgrno indicates outgroup */
Static boolean usertree;
Static steptr weight, oldweight, alias, ally, location;
Static pointptr treenode;            /* pointers to all nodes in tree */
Static Char **nayme;                 /* names of species              */
Static sequence y;
Static double threshold;
Static short *enterorder;
static int maxtrees ;         /* maximum number of tied trees stored     */

/* local variables for Pascal maketree, propagated globally for C  version: */

short nextree, which, minwhich;
double like_d, minsteps_d, bestyet_d, bestlike_d, bstlike2_d;
boolean lastrearr_d, recompute_d;
double nsteps[maxuser];
static short **fsteps;
node *there_d;
short *place_d;
short **bestrees;
long *threshwt;
baseptr nothing;
node *temp, *temp1;
static Char ch_d;
boolean *names_d;



static void *mymalloc(size_t x)
{
void *mem;

mem = (void *)malloc(x);
if (mem==NULL) exit(1);
return mem;
}


Static Void doinit()
{
  /* initializes variables */
  short i;
  node *p, *q;


  y = (Char **)Malloc(spp*sizeof(Char *));
  for (i = 0; i < spp; i++)
    y[i] = (Char *)Malloc((chars+1)*sizeof(Char));
  treenode = (pointptr)Malloc(nonodes*sizeof(node *));
  for (i = 0; i < nonodes; i++)
    treenode[i] = (node *)Malloc(sizeof(node));
  for (i = spp; i < nonodes; i++) {
    q = NULL;
    for (j = 1; j <= 3; j++) {
      p = (node *)Malloc(sizeof(node));
      p->next = q;
      q = p;
    }
    p->next->next->next = p;
    treenode[i] = p;
  }
}  /* doinit*/




Local Void sitesort()
{
  /* Shell sort keeping sites, weights in same order */
  short gap, i, j, jj, jg, k, itemp;
  boolean flip, tied;

  gap = chars / 2;
  while (gap > 0) {
    for (i = gap + 1; i <= chars; i++) {
      j = i - gap;
      flip = true;
      while (j > 0 && flip) {
        jj = alias[j - 1];
        jg = alias[j + gap - 1];
        tied = true;
        k = 1;
        while (k <= spp && tied) {
          flip = (y[k - 1][jj - 1] > y[k - 1][jg - 1]);
          tied = (tied && y[k - 1][jj - 1] == y[k - 1][jg - 1]);
          k++;
        }
        if (!flip)
          break;
        itemp = alias[j - 1];
        alias[j - 1] = alias[j + gap - 1];
        alias[j + gap - 1] = itemp;
        itemp = weight[j - 1];
        weight[j - 1] = weight[j + gap - 1];
        weight[j + gap - 1] = itemp;
        j -= gap;
      }
    }
    gap /= 2;
  }
}  /* sitesort */

Local Void sitecombine()
{
  /* combine sites that have identical patterns */
  short i, j, k;
  boolean tied;

  i = 1;
  while (i < chars) {
    j = i + 1;
    tied = true;
    while (j <= chars && tied) {
      k = 1;
      while (k <= spp && tied) {
        tied = (tied &&
            y[k - 1][alias[i - 1] - 1] == y[k - 1][alias[j - 1] - 1]);
        k++;
      }
      if (tied) {
        weight[i - 1] += weight[j - 1];
        weight[j - 1] = 0;
        ally[alias[j - 1] - 1] = alias[i - 1];
      }
      j++;
    }
    i = j - 1;
  }
}  /* sitecombine */

Local Void sitescrunch()
{
  /* move so one representative of each pattern of
     sites comes first */
  short i, j, itemp;
  boolean done, found;

  done = false;
  i = 1;
  j = 2;
  while (!done) {
    if (ally[alias[i - 1] - 1] != alias[i - 1]) {
      if (j <= i)
        j = i + 1;
      if (j <= chars) {
        found = false;
        do {
          found = (ally[alias[j - 1] - 1] == alias[j - 1]);
          j++;
        } while (!(found || j > chars));
        if (found) {
          j--;
          itemp = alias[i - 1];
          alias[i - 1] = alias[j - 1];
          alias[j - 1] = itemp;
          itemp = weight[i - 1];
          weight[i - 1] = weight[j - 1];
          weight[j - 1] = itemp;
        } else
          done = true;
      } else
        done = true;
    }
    i++;
    done = (done || i >= chars);
  }
}  /* sitescrunch */

Local Void makeweights()
{
  /* make up weights vector to avoid duplicate computations */
  short i;

  for (i = 1; i <= chars; i++) {
    alias[i - 1] = i;
    oldweight[i - 1] = weight[i - 1];
    ally[i - 1] = i;
  }
  sitesort();
  sitecombine();
  sitescrunch();
  endsite = 0;
  for (i = 1; i <= chars; i++) {
    if (ally[i - 1] == i)
      endsite++;
  }
  for (i = 1; i <= endsite; i++)
    location[alias[i - 1] - 1] = i;
  threshold = spp;
  threshwt = (long *)Malloc(endsite*sizeof(long));
  for (i = 0; i < endsite; i++) {
    weight[i] *= 10;
    threshwt[i] = (long)(threshold * weight[i] + 0.5);
  }
}  /* makeweights */

Local Void makevalues()
{
  /* set up fractional likelihoods at tips */
  short i, j;
  short ns;
  node *p;

  for (i = 1; i <= nonodes; i++) {
    treenode[i-1]->back = NULL;
    treenode[i-1]->tip = (i <= spp);
    treenode[i-1]->index = i;
    if (i > spp) {
      p = treenode[i-1]->next;
      while (p != treenode[i-1]) {
        p->back = NULL;
        p->tip = false;
        p->index = i;
        p = p->next;
      }
    }
  }
  for (i = 0; i < nonodes; i++) {
    treenode[i]->numsteps = (stepshortptr)Malloc(endsite*sizeof(short));
    treenode[i]->base = (baseptr)Malloc(endsite*sizeof(short));
  }
  for (i = spp; i < nonodes; i++) {
    p = treenode[i];
    for (j = 1; j <= 3; j++) {
      p->numsteps = (stepshortptr)Malloc(endsite*sizeof(short));
      p->base = (baseptr)Malloc(endsite*sizeof(short));
      p = p->next;
    }
  }
  for (j = 0; j < endsite; j++) {
    for (i = 0; i < spp; i++) {
      switch (y[i][alias[j] - 1]) {

      case 'A':
        ns = (short)(1 << A);
        break;

      case 'C':
        ns = (short)(1 << C);
        break;

      case 'G':
        ns = (short)(1 << G);
        break;

      case 'U':
        ns = (short)(1 << U);
        break;

      case 'T':
        ns = (short)(1 << U);
        break;

      case 'M':
        ns = ((short)(1 << A)) | ((short)(1 << C));
        break;

      case 'R':
        ns = ((short)(1 << A)) | ((short)(1 << G));
        break;

      case 'W':
        ns = ((short)(1 << A)) | ((short)(1 << U));
        break;

      case 'S':
        ns = ((short)(1 << C)) | ((short)(1 << G));
        break;

      case 'Y':
        ns = ((short)(1 << C)) | ((short)(1 << U));
        break;

      case 'K':
        ns = ((short)(1 << G)) | ((short)(1 << U));
        break;

      case 'B':
        ns = ((short)(1 << C)) | ((short)(1 << G)) | ((short)(1 << U));
        break;

      case 'D':
        ns = ((short)(1 << A)) | ((short)(1 << G)) | ((short)(1 << U));
        break;

      case 'H':
        ns = ((short)(1 << A)) | ((short)(1 << C)) | ((short)(1 << U));
        break;

      case 'V':
        ns = ((short)(1 << A)) | ((short)(1 << C)) | ((short)(1 << G));
        break;

      case 'N':
        ns = ((short)(1 << A)) | ((short)(1 << C)) | ((short)(1 << G)) |
             ((short)(1 << U));
        break;

      case 'X':
        ns = ((short)(1 << A)) | ((short)(1 << C)) | ((short)(1 << G)) |
             ((short)(1 << U));
        break;

      case '?':
        ns = ((short)(1 << A)) | ((short)(1 << C)) | ((short)(1 << G)) |
             ((short)(1 << U)) | (short)(1 << O);
        break;

      case 'O':
        ns = (short)(1 << O);
        break;

      case '-':
        ns = (short)(1 << O);
        break;
      }
      treenode[i]->base[j] = ns;
      treenode[i]->numsteps[j] = 0;
    }
  }
}  /* makevalues */


Static Void doinput()
{
  /* reads the input data */

  /*inputoptions();*/
  /*inputdata();*/
  makeweights();
  makevalues();
}  /* doinput */


Local Void fillin(node* p, node* left, node* rt)
{
  /* sets up for each node in the tree the base sequence
     at that point and counts the changes.  The program
     spends much of its time in this PROCEDURE */
  short i;
  short ns, rs, ls;

  for (i = 0; i < endsite; i++) {
    if (left) ls = left->base[i];
    if (rt) rs = rt->base[i];
    if (!left) {
      ns = rs;
      p->numsteps[i] = rt->numsteps[i];
    } else if (!rt) {
      ns = ls;
      p->numsteps[i] = left->numsteps[i];
    } else {
      ns = ls & rs;
      p->numsteps[i] = left->numsteps[i] + rt->numsteps[i];
    }
    if (ns == 0) {
      if (left && rt) ns = ls | rs;
      p->numsteps[i] += weight[i];
    }
    p->base[i] = ns;
  }
}  /* fillin */

Local Void preorder(node* p)
{
  /* recompute number of steps in preorder taking both ancestoral and
     descendent steps into account */

  if (p && !p->tip) {
    fillin (p->next, p->next->next->back, p->back);
    fillin (p->next->next, p->back, p->next->back);
    preorder (p->next->back);
    preorder (p->next->next->back);
  }
} /* preorder */

Local Void add(node* below, node* newtip, node* newfork)
{
  /* inserts the nodes newfork and its left descendant, newtip,
     to the tree.  below becomes newfork's right descendant */

  if (below != treenode[below->index - 1])
    below = treenode[below->index - 1];
  if (below->back != NULL)
    below->back->back = newfork;
  newfork->back = below->back;
  below->back = newfork->next->next;
  newfork->next->next->back = below;
  newfork->next->back = newtip;
  newtip->back = newfork->next;
  if (root == below)
    root = newfork;
  root->back = NULL;
  if (!recompute_d)
    return;
  fillin(newfork, newfork->next->back, newfork->next->next->back);
  preorder(newfork);
  if (newfork != root)
    preorder(newfork->back);
}  /* add */

Local Void re_move(node** item, node** fork)
{
  /* removes nodes item and its ancestor, fork, from the tree.
     the new descendant of fork's ancestor is made to be
     fork's second descendant (other than item).  Also
     returns pointers to the deleted nodes, item and fork */
  node *p, *q, *other;

  if ((*item)->back == NULL) {
    *fork = NULL;
    return;
  }
  *fork = treenode[(*item)->back->index - 1];
  if (*item == (*fork)->next->back)
    other = (*fork)->next->next->back;
  else
    other = (*fork)->next->back;
  if (root == *fork)
    root = other;
  p = (*item)->back->next->back;
  q = (*item)->back->next->next->back;
  if (p != NULL)
    p->back = q;
  if (q != NULL)
    q->back = p;
  (*fork)->back = NULL;
  p = (*fork)->next;
  while (p != *fork) {
    p->back = NULL;
    p = p->next;
    (*item)->back = NULL;
  }
  if (!recompute_d)
    return;
  preorder(other);
  if (other != root)
    preorder(other->back);
}  /* remove */

Local Void evaluate(node* r)
{
  /* determines the number of steps needed for a tree. this is
     the minimum number of steps needed to evolve sequences on
     this tree */
  short i, steps;
  long term;
  double sum;

  if (tree_build_interrupted) {
    longjmp(lngjmp_env, 0);
    }
  sum = 0.0;
  for (i = 0; i < endsite; i++) {
    steps = r->numsteps[i];
    if ((long)steps <= threshwt[i])
      term = steps;
    else
      term = threshwt[i];
    sum += term;
    if (usertree && which <= maxuser)
      fsteps[which - 1][i] = term;
  }
  if (usertree && which <= maxuser) {
    nsteps[which - 1] = sum;
    if (which == 1) {
      minwhich = 1;
      minsteps_d = sum;
    } else if (sum < minsteps_d) {
      minwhich = which;
      minsteps_d = sum;
    }
  }
  like_d = -sum;
}  /* evaluate */

Local Void postorder(node* p)
{
  /* traverses a binary tree, calling PROCEDURE fillin at a
     node's descendants before calling fillin at the node */
  if (p->tip)
    return;
  postorder(p->next->back);
  postorder(p->next->next->back);
  fillin(p, p->next->back, p->next->next->back);
}  /* postorder */

Local Void reroot(node* outgroup)
{
  /* reorients tree, putting outgroup in desired position. */
  node *p, *q;

  if (outgroup->back->index == root->index)
    return;
  p = root->next;
  q = root->next->next;
  p->back->back = q->back;
  q->back->back = p->back;
  p->back = outgroup;
  q->back = outgroup->back;
  outgroup->back->back = root->next->next;
  outgroup->back = root->next;
}  /* reroot */

Local Void savetraverse(node* p)
{
  /* sets BOOLEANs that indicate which way is down */
  p->bottom = true;
  if (p->tip)
    return;
  p->next->bottom = false;
  savetraverse(p->next->back);
  p->next->next->bottom = false;
  savetraverse(p->next->next->back);
}  /* savetraverse */

Local Void savetree()
{
  /* record in place where each species has to be
     added to reconstruct this tree */
  short i, j;
  node *p;
  boolean done;

  reroot(treenode[0]);
  savetraverse(root);
  for (i = 0; i < nonodes; i++)
    place_d[i] = 0;
  place_d[root->index - 1] = 1;
  for (i = 1; i <= spp; i++) {
    p = treenode[i - 1];
    while (place_d[p->index - 1] == 0) {
      place_d[p->index - 1] = i;
      while (!p->bottom)
        p = p->next;
      p = p->back;
    }
    if (i > 1) {
      place_d[i - 1] = place_d[p->index - 1];
      j = place_d[p->index - 1];
      done = false;
      while (!done) {
        place_d[p->index - 1] = spp + i - 1;
        while (!p->bottom)
          p = p->next;
        p = p->back;
        done = (p == NULL);
        if (!done)
          done = (place_d[p->index - 1] != j);
      }
    }
  }
}  /* savetree */

Local Void findtree(boolean* found, short* pos)
{
  /* finds tree given by ARRAY place in ARRAY
     bestrees by binary search */
  short i, lower, upper;
  boolean below, done;

  below = false;
  lower = 1;
  upper = nextree - 1;
  (*found) = false;
  while (!(*found) && lower <= upper) {
    (*pos) = (lower + upper) / 2;
    i = 3;
    done = false;
    while (!done) {
      done = (i > spp);
      if (!done)
        done = (place_d[i - 1] != bestrees[(*pos) - 1][i - 1]);
      if (!done)
        i++;
    }
    (*found) = (i > spp);
    below = (place_d[i - 1] <  bestrees[(*pos )- 1][i - 1]);
    if (*found)
      break;
    if (below)
      upper = (*pos) - 1;
    else
      lower = (*pos) + 1;
  }
  if (!(*found) && !below)
    (*pos)++;
}  /* findtree */

Local Void addtree(short pos)
{
  /* puts tree from ARRAY place in its proper position
     in ARRAY bestrees */
  short i;

  for (i = nextree - 1; i >= pos; i--)
    memcpy(bestrees[i], bestrees[i - 1], nonodes * sizeof(short));
  for (i = 0; i < spp; i++)
    bestrees[pos - 1][i] = place_d[i];
  nextree++;
}  /* addtree */

Local Void tryadd(node* p, node** item, node** nufork)
{
  /* temporarily adds one fork and one tip to the tree.
     if the location where they are added yields greater
     "likelihood" than other locations tested up to that
     time, then keeps that location as there */
  short pos;
  boolean found;
  node *rute, *q;

  if (p == root)
    fillin(temp, *item, p);
  else {
    fillin(temp1, *item, p);
    fillin(temp, temp1, p->back);
  }
  evaluate(temp);
  if (lastrearr_d) {
    if (like_d < bestlike_d) {
      if ((*item) == (*nufork)->next->next->back) {
        q = (*nufork)->next;
        (*nufork)->next = (*nufork)->next->next;
        (*nufork)->next->next = q;
        q->next = (*nufork);
      }
    } else if (like_d >= bstlike2_d) {
      recompute_d = false;
      add(p, *item,*nufork);
      rute = root->next->back;
      savetree();
      reroot(rute);
      if (like_d > bstlike2_d) {
        bestlike_d = bstlike2_d = like_d;
        pos = 1;
        nextree = 1;
        addtree(pos);
      } else {
        pos = 0;
        findtree(&found,&pos);
        if (!found) {
          if (nextree <= maxtrees)
            addtree(pos);
        }
      }
      re_move(item, nufork);
      recompute_d = true;
    }
  }
  if (like_d > bestyet_d) {
    bestyet_d = like_d;
    there_d = p;
  }
}  /* tryadd */

Local Void addpreorder(node* p, node* item, node* nufork)
{
  /* traverses a binary tree, calling PROCEDURE tryadd
     at a node before calling tryadd at its descendants */

  if (p == NULL)
    return;
  tryadd(p, &item,&nufork);
  if (!p->tip) {
    addpreorder(p->next->back, item, nufork);
    addpreorder(p->next->next->back, item, nufork);
  }
}  /* addpreorder */



Local Void tryrearr(node* p, boolean* success)
{
  /* evaluates one rearrangement of the tree.
     if the new tree has greater "likelihood" than the old
     one sets success = TRUE and keeps the new tree.
     otherwise, restores the old tree */
  node *frombelow, *whereto, *forknode, *q;
  double oldlike;

  if (p->back == NULL)
    return;
  forknode = treenode[p->back->index - 1];
  if (forknode->back == NULL)
    return;
  oldlike = bestyet_d;
  if (p->back->next->next == forknode)
    frombelow = forknode->next->next->back;
  else
    frombelow = forknode->next->back;
  whereto = treenode[forknode->back->index - 1];
  if (whereto->next->back == forknode)
    q = whereto->next->next->back;
  else
    q = whereto->next->back;
  fillin(temp1, frombelow, q);
  fillin(temp, temp1, p);
  fillin(temp1, temp, whereto->back);
  evaluate(temp1);
  if (like_d <= oldlike) {
    if (p != forknode->next->next->back)
      return;
    q = forknode->next;
    forknode->next = forknode->next->next;
    forknode->next->next = q;
    q->next = forknode;
    return;
  }
  recompute_d = false;
  re_move(&p, &forknode);
  fillin(whereto, whereto->next->back, whereto->next->next->back);
  recompute_d = true;
  add(whereto, p, forknode);
  (*success) = true;
  bestyet_d = like_d;
}  /* tryrearr */

Local Void repreorder(node* p, boolean* success)
{
  /* traverses a binary tree, calling PROCEDURE tryrearr
     at a node before calling tryrearr at its descendants */
  if (p == NULL)
    return;
  tryrearr(p, success);
  if (!p->tip) {
    repreorder(p->next->back,success);
    repreorder(p->next->next->back,success);
  }
}  /* repreorder */

Local Void rearrange(node** r)
{
  /* traverses the tree (preorder), finding any local
     rearrangement which decreases the number of steps.
     if traversal succeeds in increasing the tree's
     "likelihood", PROCEDURE rearrange runs traversal again */
  boolean success=true;

  while (success) {
    success = false;
    repreorder(*r, &success);
  }
}  /* rearrange */



Local Void treeout(node* p, char* treefile)
{
  /* write out file with representation of final tree */
  short n;
  
  if (p->tip) {
	  n = strlen(nayme[p->index - 1]);
	  while(nayme[p->index - 1][n-1] == ' ') n--;
	memcpy(treefile + col, nayme[p->index - 1], n );
    col += n;
  } else {
	  treefile[col]='(';
    col++;
    treeout(p->next->back, treefile);
	  treefile[col]=',';
    col++;
    treeout(p->next->next->back, treefile);
	  treefile[col]=')';
    col++;
  }
  if (p == root) { strcpy(treefile + col, ";\n"); col += 2; }
}  /* treeout */


static char *describe(void)
{
  col = 0;
	char *treefile = (char *)malloc(spp*(nmlngth + 3) + 10);
  treeout(root, treefile);
	treefile = (char *)realloc(treefile, col + 1);
	return treefile;
}  /* describe */




Local int findch(Char c)
//returns 0 iff OK
{
  /* scan forward until find character c */
  boolean done;

  done = false;
  while (!(done)) {
    if (c == ',') {
      if (ch_d == '(' || ch_d == ')' || ch_d == ';') {
//        printf("\nERROR IN USER TREE%3hd: UNMATCHED PARENTHESIS OR MISSING COMMA\n",  which);
//	exit(-1);
		  return 1;
      } else if (ch_d == ',')
        done = true;
    } else if (c == ')') {
      if (ch_d == '(' || ch_d == ',' || ch_d == ';') {
//        printf("\nERROR IN USER TREE%3hd: ",which);
//	printf("UNMATCHED PARENTHESIS OR NON-BIFURCATED NODE\n");
//	exit(-1);
		  return 1;
      } else {
        if (ch_d == ')')
          done = true;
      }
    } else if (c == ';') {
      if (ch_d != ';') {
//        printf("\nERROR IN USER TREE%3hd: ",which);
//	printf("UNMATCHED PARENTHESIS OR MISSING SEMICOLON\n");
//	exit(-1);
		  return 1;
      } else
        done = true;
    }
    if ( ch_d != ')' && done)
      continue;
	sscanf(infile++, "%c", &ch_d);
    if (ch_d == '\n')
      ch_d = ' ';
  }
	return 0;
}  /* findch */


Local int addelement(node** p, short* nextnode, short* lparens, boolean* names_d)
//returns 0 iff OK
{
  /* recursive procedure adds nodes to user-defined tree */
  node *q;
  short i, n;
  boolean found;
  Char str[nmlngth];

  do {
    sscanf(infile++, "%c", &ch_d);
    if (ch_d == '\n')
      ch_d = ' ';
  } while (ch_d == ' ');
  if (ch_d == '(' ) {
    if ((*lparens) >= spp - 1) {
//      printf("\nERROR IN USER TREE: TOO MANY LEFT PARENTHESES\n");
//     exit(-1);
		return 1;
    }
    (*nextnode)++;
    (*lparens)++;
    q = treenode[(*nextnode) - 1];
    if( addelement(&q->next->back, nextnode,lparens,names_d) ) return 1;
    q->next->back->back = q->next;
    if( findch(',') ) return 1;
    if( addelement(&q->next->next->back,nextnode,lparens,names_d) )return 1;
    q->next->next->back->back = q->next->next;
    if( findch(')') ) return 1;
    *p = q;
    return 0;
  }
  for (i = 0; i < nmlngth; i++)
    str[i] = ' ';
  n = 1;
  do {
//    if (ch_d == '_') ch_d = ' ';
    str[n - 1] = ch_d; 
    sscanf(infile++, "%c", &ch_d);
    if (ch_d == '\n')
     ch_d = ' ';
    n++;
  } while (ch_d != ',' && ch_d != ')' && ch_d != ':' && n <= nmlngth);
  n = 1;
  do {
    found = true;
    for (i = 0; i < nmlngth; i++)
      found = (found && str[i] == nayme[n - 1][i]);
    if (found) {
      if (names_d[n - 1] == false) {
        *p = treenode[n - 1];
        names_d[n - 1] = true;
      } else {
//	exit(-1);
		  return 1;
      }
    } else
      n++;
  } while (!(n > spp || found));
  if (n <= spp) return 0;
	return 1;
}  /* addelement */


Local int treeread()
//returns 0 iff OK
{
  /* read in user-defined tree and set it up */
  short i, nextnode,lparens;

  root = treenode[spp];
  nextnode = spp;
  root->back = NULL;
  for (i = 0; i < spp; i++)
    names_d[i] = false;
  lparens = 0;
  if( addelement(&root, &nextnode,&lparens,names_d) ) return 1;
	if( findch(';') ) return 1;;
  if(*infile != 0) {
		sscanf(infile, "%*[^\n]");
		infile++;
		}
	return 0;
}  /* treeread */



Static char *maketree(void)
//returns NULL iff error
{
  /* constructs a binary tree from the pointers in treenode.
     adds each node at location which yields highest "likelihood"
     then rearranges the tree for greatest "likelihood" */
  short i, j;
  double gotlike;
  node *item, *nufork, *dummy;
	char *treefile, *onetree;
	int ltreefile = 0;

	treefile = (char *)malloc(100);
	treefile[0] = 0;
  temp = (node *)Malloc(sizeof(node));
  temp->numsteps = (stepshortptr)Malloc(endsite*sizeof(short));
  temp->base = (baseptr)Malloc(endsite*sizeof(short)); 
  temp1 = (node *)Malloc(sizeof(node));
  temp1->numsteps = (stepshortptr)Malloc(endsite*sizeof(short));
  temp1->base = (baseptr)Malloc(endsite*sizeof(short));

  if (setjmp(lngjmp_env)) goto way_out;

  if (!usertree) {
    recompute_d = true;
    for (i = 1; i <= spp; i++)
      enterorder[i - 1] = i;
    root = treenode[enterorder[0] - 1];
    add(treenode[enterorder[0] - 1], treenode[enterorder[1] - 1],
        treenode[spp]);
    lastrearr_d = false;
    for (i = 3; i <= spp; i++) {
      bestyet_d = -10.0 * spp * chars;
      item = treenode[enterorder[i - 1] - 1];
      nufork = treenode[spp + i - 2];
      there_d = root;
      addpreorder(root, item, nufork);
      add(there_d, item, nufork);
      like_d = bestyet_d;
      rearrange(&root);
      lastrearr_d = (i == spp);
      if (lastrearr_d) {
        bestlike_d = bestyet_d;
        
        bstlike2_d = bestlike_d;
        nextree = 1;
        
        do {
          gotlike = bestlike_d;
          for (j = 0; j < nonodes; j++) {
            there_d = root;
            bestyet_d = -10.0 * spp * chars;
            item = treenode[j];

            if (item != root) {
              re_move(&item, &nufork);
              there_d = root;
             addpreorder(root, item, nufork);
             add(there_d, item, nufork);
            }

          }
        } while (bestlike_d > gotlike);
      }
    }

    for (i = spp - 1; i >= 1; i--)
      re_move(&treenode[i], &dummy);
    
    if (nextree > maxtrees + 1) 
      nextree = maxtrees + 1;
      
    recompute_d = false;
    for (i = 0; i <= (nextree - 2); i++) {
      root = treenode[0];
      add(treenode[0], treenode[1], treenode[spp]);
      for (j = 3; j <= spp; j++)
        add(treenode[bestrees[i][j - 1] - 1], treenode[j - 1],
          treenode[spp + j - 2]);
      reroot(treenode[0]);
      postorder(root);
      evaluate(root);
      onetree = describe();
		ltreefile += strlen(onetree);
		treefile = realloc(treefile, ltreefile + 1);
		strcat(treefile, onetree);
		free(onetree);
      for (j = 1; j < spp; j++)
        re_move(&treenode[j], &dummy);
    }
  }



 else {
    fsteps = (short **)Malloc(maxuser*sizeof(short *));
    for (j = 1; j <= maxuser; j++)
      fsteps[j - 1] = (short *)Malloc(endsite*sizeof(short));
    which = 1;
    free(treefile); treefile = NULL;
    if( treeread() == 0) {
		postorder(root);
		evaluate(root);
		treefile = describe();
		}
 }
way_out:
  if (usertree) {
    for (j = 1; j <= maxuser; j++) free(fsteps[j - 1]);
    free(fsteps);
    }
  free(temp->numsteps);
  free(temp->base);
  free(temp);
  free(temp1->numsteps);
  free(temp1->base);
  free(temp1);
  return treefile;
}  /* maketree */





/*******************************************************/
/************************ MAIN *************************/
/*******************************************************/

char* dnapars(char** seq, char** seqname, int notu, int *steps, char* toevaluate, int arg_maxtrees,
		    int *bt_weights)
{
char* treefile;
int ll;
maxtrees = arg_maxtrees;
if (toevaluate){ usertree=true; infile=toevaluate; }
else usertree=false;

spp=notu;
if (seq) {
  ll = strlen(seq[0]);
  if (ll >= SHRT_MAX) return NULL;
  chars = (short)ll;
  }
else return NULL;
nonodes = spp * 2 - 1;
  doinit();

  bestrees = (short **)Malloc(maxtrees*sizeof(short *));
  for (j = 1; j <= maxtrees; j++)
    bestrees[j - 1] = (short *)Malloc(nonodes*sizeof(short));
  nayme = (Char **)Malloc(spp*sizeof(Char *));
	nmlngth = 0;
  for (j = 0; j < spp; j++) {
		if(strlen(seqname[j]) > nmlngth) nmlngth = strlen(seqname[j]);
		}
  for (j = 1; j <= spp; j++)
    nayme[j - 1] = (Char *)Malloc((nmlngth+1)*sizeof(Char));
  enterorder = (short *)Malloc(spp*sizeof(short));
  names_d = (boolean *)Malloc(spp*sizeof(boolean));
  place_d = (short *)Malloc(nonodes*sizeof(short));
  weight = (short *)Malloc(chars*sizeof(short));
  oldweight = (short *)Malloc(chars*sizeof(short));
  alias = (short *)Malloc(chars*sizeof(short));
  ally = (short *)Malloc(chars*sizeof(short));
  location = (short *)Malloc(chars*sizeof(short));

for(i=0;i<spp;i++){
  for(j=0;j<chars;j++)
    y[i][j]=seq[i][j];
  strncpy(nayme[i],seqname[i],nmlngth);
  ll=strlen(seqname[i]);
  if(ll>nmlngth) ll=nmlngth;
  for(j=ll;j<nmlngth;j++) nayme[i][j]=' ';
  nayme[i][nmlngth]='\0';
}
	if(bt_weights != NULL) for (i = 0; i < chars; i++) weight[i] = bt_weights[i];
	else for (i = 0; i < chars; i++) weight[i] = 1;
    doinput();
    treefile = maketree();

    for (i = 0; i < spp; i++) {
      free(treenode[i]->numsteps);
      free(treenode[i]->base);
      free(y[i]);
      free(nayme[i]);
    }
    free(y);
    free(enterorder);
    free(names_d);
    free(place_d);
    free(weight);
    free(oldweight);
    free(alias);
    free(ally);
    free(location);
	free(nayme);
	for (j = 0; j < maxtrees; j++) free(bestrees[j]);
	free(bestrees);

    for (i = spp; i < nonodes; i++) {
      p = treenode[i];
      for (j = 1; j <= 3; j++) {
	free(p->numsteps);
	free(p->base);
	p = p->next;
      }
    }
    free(treenode);
    free(threshwt);

if (steps) *steps = (int)(like_d/(-10.) + .5);
return treefile;
}  /* DNA parsimony by uphill search */



