/**
 * Author: Mark Larkin
 * 
 * Copyright (c) 2007 Des Higgins, Julie Thompson and Toby Gibson.  
 */
#ifdef HAVE_CONFIG_H
    #include "config.h"
#endif
#include <sstream>
#include "Tree.h"

namespace clustalw
{

/**
 * 
 * @param firstSeq 
 * @param lastSeq 
 * @param sweight 
 */
 
void Tree::calcSeqWeights(int firstSeq, int lastSeq, vector<int>* sweight)
{
    if((int)sweight->size() < lastSeq - 1)
    {
        sweight->resize(lastSeq - 1);
    }
    
    int i, _nSeqs;
    int temp, sum;
    int *weight;
    /*
     * If there are more than three sequences....
     */
    _nSeqs = lastSeq - firstSeq;
    if ((_nSeqs >= 2) && (clustalw::userParameters->getDistanceTree() == true) && 
        (clustalw::userParameters->getNoWeights() == false))
    {
        /*
         * Calculate sequence weights based on Phylip tree.
         */

        weight = new int[lastSeq + 1];
        
        for (i = firstSeq; i < lastSeq; i++)
        {
            weight[i] = calcWeight(i);
        }

        /*
         * Normalise the weights, such that the sum of the weights = INT_SCALE_FACTOR
         */

        sum = 0;
        for (i = firstSeq; i < lastSeq; i++)
        {
            sum += weight[i];
        }

        if (sum == 0)
        {
            for (i = firstSeq; i < lastSeq; i++)
            {
                weight[i] = 1;
            }
            sum = i;
        }

        for (i = firstSeq; i < lastSeq; i++)
        {
            (*sweight)[i] = (weight[i] * INT_SCALE_FACTOR) / sum;
            if ((*sweight)[i] < 1)
            {
                (*sweight)[i] = 1;
            }
        }
        
        delete []weight;
        weight = NULL;
    }

    else
    {
        /*
         * Otherwise, use identity weights.
         */
        temp = INT_SCALE_FACTOR / _nSeqs;
        // AW 2009-05-28: goes wrong if we have more than
        // INT_SCALE_FACTOR seqs. if so, set to 1, just as above
        if (temp < 1)
            temp = 1;

        for (i = firstSeq; i < lastSeq; i++)
        {
            (*sweight)[i] = temp;
        }
    }

}

/**
 * 
 * @param alignPtr 
 * @param treeFileName 
 * @param firstSeq 
 * @param lastSeq 
 * @return 
 */
int Tree::readTree(clustalw::Alignment* alignPtr, const string& treeFileName, int firstSeq, int lastSeq)
{

    if(alignPtr == NULL || firstSeq < 0 || lastSeq < 1)
    {
        return 0;
    }
    char c;
    string name1 = "", name2 = "";
    int i, j, k;
    bool found;

    numSeq = 0;
    nnodes = 0;
    ntotal = 0;
    rootedTree = true;

    // Need to check what happens if I try to open a file that doesnt exist!
    if(treeFileName.empty())
    {
        return 0; // Cannot open, empty string!
    }
    else
    {
        file.open(treeFileName.c_str(), ifstream::in);
        if(!file.is_open())
        {
            clustalw::utilityObject->error("Cannot open output file [%s]\n", treeFileName.c_str());
        }
    }

    skipSpace(&file);
    charFromFile = file.get();
    
    if (charFromFile != '(')
    {
        clustalw::utilityObject->error("Wrong format in tree file %s\n", treeFileName.c_str());
        return 0;
    }
    file.seekg(0, ios::beg); // Put get pointer back to the begining!

    clustalw::userParameters->setDistanceTree(true);

    
    // Allocate memory for tree
    
    // Allocate memory.
    nptr = new TreeNode*[3 * (lastSeq - firstSeq + 1)];
    ptrs = new TreeNode*[3 * (lastSeq - firstSeq + 1)];
    lptr = new TreeNode*[lastSeq - firstSeq + 1];
    olptr = new TreeNode*[lastSeq + 1];
    
    seqTree = avail();
    setInfo(seqTree, NULL, 0, string(""), 0.0);

    createTree(seqTree, NULL, &file);
    file.close();

    if (numSeq != lastSeq - firstSeq)
    {
        stringstream ss;
        ss << "tree not compatible with alignment\n(" << lastSeq - firstSeq 
        << " sequences in alignment and "<< numSeq <<" in tree\n";
        string errorMes;
        ss >> errorMes;
        clustalw::utilityObject->error(errorMes.c_str());
        return 0;
    }
   
    // If the tree is unrooted, reroot the tree - ie. minimise the difference
    // between the mean root->leaf distances for the left and right branches of
    // the tree.     

    if (clustalw::userParameters->getDistanceTree() == false)
    {
        if (rootedTree == false)
        {
            clustalw::utilityObject->error("input tree is unrooted and has no distances.\nCannot align sequences");
            return 0;
        }
    }

    if (rootedTree == false)
    {
        root = reRoot(seqTree, lastSeq - firstSeq + 1);
    }
    else
    {
        root = seqTree;
    }
   
    // calculate the 'order' of each node.
    int nameLength;
    string nameSeq;
    orderNodes();

    if (numSeq >= 2)
    {
        // If there are more than three sequences....
        // assign the sequence nodes (in the same order as in the alignment file)
        for (i = firstSeq; i < lastSeq; i++)
        {
            nameLength = alignPtr->getName(i + 1).length();
            nameSeq = alignPtr->getName(i + 1);
            name1 = "";
            name2 = "";
            if (nameLength > clustalw::MAXNAMES)
            {
                stringstream ss;
                ss << "name " << nameSeq << " is too long for PHYLIP tree format (max " 
                   << clustalw::MAXNAMES << " chars)";
                string msg; 
                ss >> msg; 
                clustalw::utilityObject->warning(msg.c_str());// Mark change 17-5-07
            }

            for (k = 0; k < nameLength && k < clustalw::MAXNAMES; k++)
            {
                c = nameSeq[k];
                if ((c > 0x40) && (c < 0x5b)) 
                {
                    c = c | 0x20;
                }
                if (c == ' ')
                {
                    c = '_';
                }
                name2 += c;
            }
            found = false;

            for (j = 0; j < numSeq; j++)
            {
                name1 = "";
                for (k = 0; k < (int)lptr[j]->name.length() && k < clustalw::MAXNAMES; k++)
                {
                    c = lptr[j]->name[k];
                    if ((c > 0x40) && (c < 0x5b))
                    {
                        c = c | 0x20;
                    }

                    name1 += c;
                }

                if (name1.compare(name2) == 0)
                {
                    olptr[i] = lptr[j];
                    found = true;
                }
            }

            if (found == false)
            {
                utilityObject->error("tree not compatible with alignment:\n %s not found\n", name2.c_str());
                return 0;
            }
        }

    }
    return (1);
}

/**
 * 
 * @param firstSeq 
 * @param lastSeq 
 * @return 
 */
auto_ptr<AlignmentSteps> Tree::createSets(int firstSeq, int lastSeq)
{
    auto_ptr<AlignmentSteps> progAlignSteps;
    progAlignSteps.reset(new AlignmentSteps);
    
    int i, j, _nSeqs;

    numSets = 0;
    _nSeqs = lastSeq - firstSeq;
    if (_nSeqs >= 2)
    {
        // If there are more than three sequences....
        groups = new int[_nSeqs + 1];
        groupSeqs(root, groups, _nSeqs, progAlignSteps.get());

        delete []groups;
        groups  = NULL;

    }

    else
    {
        groups = new int[_nSeqs + 1];
        for (i = 0; i < _nSeqs - 1; i++)
        {
            for (j = 0; j < _nSeqs; j++)
                if (j <= i)
                {
                    groups[j] = 1;
                }
                else if (j == i + 1)
                {
                    groups[j] = 2;
                }
                else
                {
                    groups[j] = 0;
                }

            progAlignSteps->saveSet(_nSeqs, groups);
        }
        delete []groups;
        groups  = NULL;
    }    
    
    return progAlignSteps;
}

/**
 * calcSimilarities changes the distMat.
 * @param alignPtr 
 * @param distMat 
 * @return 
 */
int Tree::calcSimilarities(clustalw::Alignment* alignPtr, clustalw::DistMatrix* distMat)
{
    int depth = 0, i, j, k, n;
    bool found;
    int nerrs, seq1[MAXERRS], seq2[MAXERRS];
    TreeNode *p,  **pathToRoot;
    float dist;
    float *distToNode, badDist[MAXERRS];
    double **dmat;
    ostringstream err1;
    char reply;
    int nSeqs = alignPtr->getNumSeqs();
    
    pathToRoot = new TreeNode*[nSeqs];
    distToNode = new float[nSeqs];
    dmat = new double*[nSeqs];

    for (i = 0; i < nSeqs; i++)
    {
        dmat[i] = new double[nSeqs];
    }

    if (nSeqs >= 2)
    {
        /*
         * for each leaf, determine all nodes between the leaf and the root;
         */
        for (i = 0; i < nSeqs; i++)
        {
            depth = 0;dist = 0.0;
            p = olptr[i];
            while (p != NULL)
            {
                pathToRoot[depth] = p;
                dist += p->dist;
                distToNode[depth] = dist;
                p = p->parent;
                depth++;
            }

            /*
             * for each pair....
             */
            for (j = 0; j < i; j++)
            {
                p = olptr[j];
                dist = 0.0;
                /*
                 * find the common ancestor.
                 */
                found = false;
                n = 0;
                while ((found == false) && (p->parent != NULL))
                {
                    for (k = 0; k < depth; k++)
                    if (p->parent == pathToRoot[k])
                    {
                        found = true;
                        n = k;
                    }

                    dist += p->dist;
                    p = p->parent;
                }

                dmat[i][j] = dist + distToNode[n - 1];
            }
        }

        nerrs = 0;
        for (i = 0; i < nSeqs; i++)
        {
            dmat[i][i] = 0.0;
            for (j = 0; j < i; j++)
            {
                if (dmat[i][j] < 0.01)
                {
                    dmat[i][j] = 0.01;
                }
                if (dmat[i][j] > 1.0)
                {
                    if (dmat[i][j] > 1.1 && nerrs < MAXERRS)
                    {
                        seq1[nerrs] = i;
                        seq2[nerrs] = j;
                        badDist[nerrs] = dmat[i][j];
                        nerrs++;
                    }
                    dmat[i][j] = 1.0;
                }
            }
        }
        if (nerrs > 0 && !userParameters->getGui())
        {
            string errMess = "The following sequences are too divergent to be aligned:\n";
            
            for (i = 0; i < nerrs && i < 5; i++)
            {
                err1 << "           " << alignPtr->getName(seq1[i] + 1) << " and " 
                     << alignPtr->getName(seq2[i] + 1) << " (distance " 
                     << setprecision(3) << badDist[i] << ")\n"; 
            }
            errMess += err1.str();
            errMess += "(All distances should be between 0.0 and 1.0)\n";
            errMess += "This may not be fatal but you have been warned!\n";
            errMess += "SUGGESTION: Remove one or more problem sequences and try again";
            
            if (clustalw::userParameters->getInteractive())
            {
                reply = clustalw::utilityObject->promptForYesNo(errMess.c_str(), "Continue ");
            }
            else
            {
                reply = 'y';
            }
            if ((reply != 'y') && (reply != 'Y'))
            {
                return 0;
            }
        }
    }
    else
    {
        for (i = 0; i < nSeqs; i++)
        {
            for (j = 0; j < i; j++)
            {
                dmat[i][j] = (*distMat)(i + 1, j + 1);
            }
        }
    }
    delete []pathToRoot;
    delete []distToNode;
    
    double value;
    for (i = 0; i < nSeqs; i++)
    {
        distMat->SetAt(i + 1, i + 1, 0.0);
        for (j = 0; j < i; j++)
        {
            value = 100.0 - (dmat[i][j]) * 100.0;
            distMat->SetAt(i + 1, j + 1, value);
            distMat->SetAt(j + 1, i + 1, value);
        }
    }

    for (i = 0; i < nSeqs; i++)
    {
        delete [] dmat[i];
    }

    delete []dmat;

    return 1;
    
}

/** *************************************************************************
 * Private functions!!!!!!!!!!!!!!!                                         *
 ****************************************************************************/
 
/**
 * 
 * @param ptree 
 * @param parent 
 * @param file 
 */
void Tree::createTree(clustalw::TreeNode* ptree, clustalw::TreeNode* parent, ifstream* file)
{
    TreeNode* p;

    int i, type;
    float dist;
    string name;

    
    // is this a node or a leaf ?
    skipSpace(file);
    charFromFile = file->get();
    if (charFromFile == '(')
    {
        // this must be a node....
        type = NODE;
        name = "";
        ptrs[ntotal] = nptr[nnodes] = ptree;
        nnodes++;
        ntotal++;

        createNode(ptree, parent);

        p = ptree->left;
        createTree(p, ptree, file);

        if (charFromFile == ',')
        {
            p = ptree->right;
            createTree(p, ptree, file);
            if (charFromFile == ',')
            {
                ptree = insertNode(ptree);
                ptrs[ntotal] = nptr[nnodes] = ptree;
                nnodes++;
                ntotal++;
                p = ptree->right;
                createTree(p, ptree, file);
                rootedTree = false;
            }
        }

        skipSpace(file);
        charFromFile = file->get();
    }
    
    // ...otherwise, this is a leaf
    
    else
    {
        type = LEAF;
        ptrs[ntotal++] = lptr[numSeq++] = ptree;        
        // get the sequence name
        name = "";
        name += charFromFile;
        charFromFile = file->get();
        
        i = 1;
        while ((charFromFile != ':') && (charFromFile != ',') && (charFromFile != ')'))
        {
            if (i < MAXNAMES)
            {
                name += charFromFile;
                i++;
            }
            charFromFile = file->get();
        }

        if (charFromFile != ':')
        {
            clustalw::userParameters->setDistanceTree(false);
            dist = 0.0;
        }
    }
    
    // get the distance information
    
    dist = 0.0;
    if (charFromFile == ':')
    {
        skipSpace(file);
        (*file) >> dist;
        skipSpace(file);
        charFromFile = file->get();
    }
    setInfo(ptree, parent, type, name, dist);
}

/**
 * 
 * @param pptr 
 * @param parent 
 */
void Tree::createNode(TreeNode* pptr, TreeNode* parent)
{
    TreeNode* t;
    pptr->parent = parent;
    t = avail();
    pptr->left = t;
    t = avail();
    pptr->right = t;
}

/**
 * 
 * @param pptr 
 * @return 
 */
TreeNode* Tree::insertNode(TreeNode* pptr)
{

    TreeNode* newnode;

    newnode = avail();
    createNode(newnode, pptr->parent);

    newnode->left = pptr;
    pptr->parent = newnode;

    setInfo(newnode, pptr->parent, NODE, "", 0.0);

    return newnode;
}

/**
 * 
 * @param p 
 */
void Tree::clearTree(clustalw::TreeNode* p)
{
    clearTreeNodes(p);
    delete [] nptr;
    nptr  = NULL;
    delete [] ptrs;
    ptrs  = NULL;
    delete [] lptr;
    lptr  = NULL;
    delete [] olptr;
    olptr  = NULL;
}

/**
 * 
 * @param p 
 */
void Tree::clearTreeNodes(clustalw::TreeNode* p)
{
    if (p == NULL)
    {
        p = root;
    }
    if (p->left != NULL)
    {
        clearTreeNodes(p->left);
    }
    if (p->right != NULL)
    {
        clearTreeNodes(p->right);
    }
    p->left = NULL;
    p->right = NULL;
    
    delete p;
    p  = NULL;
}




/**
 * 
 * @param 
 * @param 
 * @return 
 */
void Tree::debugPrintAllNodes(int nseqs)
{
  clustalw::TreeNode *p;
  int i;
   float diff, maxDist;
 
    cerr << "\nDEBUG: reportAllNodes\n";
    for (i = 0; i < ntotal; i++) {
        p = ptrs[i];
        //        ios::sync_with_stdio();

        // same design as TreeNode
        if (p->parent == NULL)
            diff = calcRootMean(p, &maxDist);
        else
            diff = calcMean(p, &maxDist, nseqs);
        fprintf(stdout,
                "i=%d p=%p: parent=%p left=%p right=%p dist=%f diff=%f\n",
                i, (void*)p, (void*)p->parent, (void*)p->left, (void*)p->right,
		p->dist, diff);
    }
}





/**
 * 
 * @param ptree 
 * @param nseqs 
 * @return 
 */
clustalw::TreeNode* Tree::reRoot(clustalw::TreeNode* ptree, int nseqs)
{
    clustalw::TreeNode *p, *rootNode, *rootPtr;
    float diff, minDiff = 0.0, minDepth = 1.0, maxDist;
    int i;
    bool first = true;

    // find the difference between the means of leaf->node
    // distances on the left and on the right of each node
    rootPtr = ptree;
    for (i = 0; i < ntotal; i++)
    {
        p = ptrs[i];
        if (p->parent == NULL)
        {
            /* AW Bug 94: p->parent must be chosen as rootNode
               (non-optimized executables (-O0) never do), otherwise
               insertRoot fails.
               Is p->parent == NULL valid at all?
               Simplest thing for now is to continue here. Tree code
               needs serious dismantling anyway. See debugPrintAllNodes
            */

            continue;
            //diff = calcRootMean(p, &maxDist);
        }
        else
        {
            diff = calcMean(p, &maxDist, nseqs);
        }

        if ((diff == 0) || ((diff > 0) && (diff < 2 *p->dist)))
        {
            if ((maxDist < minDepth) || (first == true))
            {
                first = false;
                rootPtr = p;
                minDepth = maxDist;
                minDiff = diff;
            }
        }

    }

    
    // insert a new node as the ancestor of the node which produces the shallowest
    // tree.
    /* AW Bug 94: could also be prevented here */
    if (rootPtr == ptree)
    {
        minDiff = rootPtr->left->dist + rootPtr->right->dist;
        rootPtr = rootPtr->right;
    }
    rootNode = insertRoot(rootPtr, minDiff);

    diff = calcRootMean(rootNode, &maxDist);

    return rootNode;
}

/**
 * 
 * @param p 
 * @param diff 
 * @return 
 */
clustalw::TreeNode* Tree::insertRoot(clustalw::TreeNode* p, float diff)
{
    clustalw::TreeNode *newp, *prev, *q, *t;
    float dist, prevDist, td;

    newp = avail();

    if (p->parent==NULL) {
        // AW bug 94: question remains if access here should be handled differently
        cerr << "\n\n*** INTERNAL ERROR: Tree::insertRoot: TreeNode p->parent is NULL\n";
        cerr << "To help us fix this bug, please send sequence file and used options to clustalw@ucd.ie\n";
        exit(1);
    }

    t = p->parent;
    prevDist = t->dist;

    p->parent = newp;

    dist = p->dist;

    p->dist = diff / 2;

    if (p->dist < 0.0)
    {
        p->dist = 0.0;
    }

    if (p->dist > dist)
    {
        p->dist = dist;
    }

    t->dist = dist - p->dist;

    newp->left = t;
    newp->right = p;
    newp->parent = NULL;
    newp->dist = 0.0;
    newp->leaf = NODE;

    if (t->left == p)
    {
        t->left = t->parent;
    }
    else
    {
        t->right = t->parent;
    }

    prev = t;
    q = t->parent;

    t->parent = newp;

    while (q != NULL)
    {
        if (q->left == prev)
        {
            q->left = q->parent;
            q->parent = prev;
            td = q->dist;
            q->dist = prevDist;
            prevDist = td;
            prev = q;
            q = q->left;
        }
        else
        {
            q->right = q->parent;
            q->parent = prev;
            td = q->dist;
            q->dist = prevDist;
            prevDist = td;
            prev = q;
            q = q->right;
        }
    }

    /*
     * remove the old root node
     */
    q = prev;
    if (q->left == NULL)
    {
        dist = q->dist;
        q = q->right;
        q->dist += dist;
        q->parent = prev->parent;

        if (prev->parent->left == prev)
        {
            prev->parent->left = q;
        }
        else
        {
            prev->parent->right = q;
        }

        prev->right = NULL;
    }
    else
    {
        dist = q->dist;
        q = q->left;
        q->dist += dist;
        q->parent = prev->parent;

        if (prev->parent->left == prev)
        {
            prev->parent->left = q;
        }
        else
        {
            prev->parent->right = q;
        }

        prev->left = NULL;
    }

    return (newp);
}

/**
 * 
 * @param root 
 * @param maxDist 
 * @return 
 */
float Tree::calcRootMean(clustalw::TreeNode* root, float *maxDist)
{
    float dist, leftSum = 0.0, rightSum = 0.0, leftMean, rightMean, diff;
    clustalw::TreeNode* p;
    int i;
    int numLeft, numRight;
    int direction;
    
    // for each leaf, determine whether the leaf is left or right of the root.

    dist = (*maxDist) = 0;
    numLeft = numRight = 0;
    for (i = 0; i < numSeq; i++)
    {
        p = lptr[i];
        dist = 0.0;
        while (p->parent != root)
        {
            dist += p->dist;
            p = p->parent;
        }

        if (p == root->left)
        {
            direction = LEFT;
        }
        else
        {
            direction = RIGHT;
        }

        dist += p->dist;

        if (direction == LEFT)
        {
            leftSum += dist;
            numLeft++;
        }
        else
        {
            rightSum += dist;
            numRight++;
        }
        if (dist > (*maxDist))
        {
            *maxDist = dist;
        }
    }

    leftMean = leftSum / numLeft;
    rightMean = rightSum / numRight;

    diff = leftMean - rightMean;
    return diff;
}

/**
 * 
 * @param nptr 
 * @param maxDist 
 * @param nSeqs 
 * @return 
 */
float Tree::calcMean(clustalw::TreeNode* nptr, float *maxDist, int nSeqs)
{
    float dist, leftSum = 0.0, rightSum = 0.0, leftMean, rightMean, diff;
    clustalw::TreeNode* p;  
    clustalw::TreeNode** pathToRoot;
    float *distToNode;
    int depth = 0, i, j, n = 0;
    int numLeft, numRight;
    int direction;
    bool found;

    pathToRoot = new clustalw::TreeNode*[nSeqs];
    distToNode = new float[nSeqs];
    // determine all nodes between the selected node and the root;
  
    depth = 0;
    (*maxDist) = dist = 0.0;
    numLeft = numRight = 0;
    p = nptr;
    while (p != NULL)
    {
        pathToRoot[depth] = p;
        dist += p->dist;
        distToNode[depth] = dist;
        p = p->parent;
        depth++;
    }

    // for each leaf, determine whether the leaf is left or right of the node.
    // (RIGHT = descendant, LEFT = not descendant)
 
    for (i = 0; i < numSeq; i++)
    {
        p = lptr[i];
        if (p == nptr)
        {
            direction = RIGHT;
            dist = 0.0;
        }
        else
        {
            direction = LEFT;
            dist = 0.0;
            
            // find the common ancestor. 
            
            found = false;
            n = 0;
            while ((found == false) && (p->parent != NULL))
            {
                for (j = 0; j < depth; j++)
                if (p->parent == pathToRoot[j])
                {
                    found = true;
                    n = j;
                }

                dist += p->dist;
                p = p->parent;
            }
            if (p == nptr)
            {
                direction = RIGHT;
            }
        }

        if (direction == LEFT)
        {
            leftSum += dist;
            leftSum += distToNode[n - 1];
            numLeft++;
        }
        else
        {
            rightSum += dist;
            numRight++;
        }

        if (dist > (*maxDist))
        {
            *maxDist = dist;
        }
    }

    delete [] distToNode;
    distToNode = NULL;
    delete [] pathToRoot;
    pathToRoot = NULL;
    
    leftMean = leftSum / numLeft;
    rightMean = rightSum / numRight;

    diff = leftMean - rightMean;
    return (diff);
}

/**
 * 
 */
void Tree::orderNodes()
{
    int i;
    clustalw::TreeNode* p;

    for (i = 0; i < numSeq; i++)
    {
        p = lptr[i];
        while (p != NULL)
        {
            p->order++;
            p = p->parent;
        }
    }
}

/**
 * 
 * @param leaf 
 * @return 
 */
int Tree::calcWeight(int leaf)
{
    clustalw::TreeNode* p;
    float weight = 0.0;

    p = olptr[leaf];
    while (p->parent != NULL)
    {
        weight += p->dist / p->order;
        p = p->parent;
    }

    weight *= 100.0;

    return ((int)weight);
}

/**
 * skipSpace is used to skip all the spaces at the begining of a file. The next read
 * will give a character other than a space.
 * @param file 
 */
void Tree::skipSpace(ifstream* file)
{
    char c;

    do
    {
        c = file->get();
    }
    while (isspace(c));

    file->putback(c);
}

/**
 * 
 * @param p 
 * @param nextGroups 
 * @param nSeqs 
 * @param stepsPtr 
 */
void Tree::groupSeqs(clustalw::TreeNode* p, int *nextGroups, int nSeqs, AlignmentSteps* stepsPtr)
{
    int i;
    int *tmpGroups;

    tmpGroups = new int[nSeqs + 1];
    for (i = 0; i < nSeqs; i++)
    {
        tmpGroups[i] = 0;
    }

    if (p->left != NULL)
    {
        if (p->left->leaf == NODE)
        {
            groupSeqs(p->left, nextGroups, nSeqs, stepsPtr);

            for (i = 0; i < nSeqs; i++)
                if (nextGroups[i] != 0)
                {
                    tmpGroups[i] = 1;
                }
        }
        else
        {
            markGroup1(p->left, tmpGroups, nSeqs);
        }

    }

    if (p->right != NULL)
    {
        if (p->right->leaf == NODE)
        {
            groupSeqs(p->right, nextGroups, nSeqs, stepsPtr);
            for (i = 0; i < nSeqs; i++)
                if (nextGroups[i] != 0)
                {
                    tmpGroups[i] = 2;
                }
        }
        else
        {
            markGroup2(p->right, tmpGroups, nSeqs);
        }
        stepsPtr->saveSet(nSeqs, tmpGroups);
    }

    for (i = 0; i < nSeqs; i++)
    {
        nextGroups[i] = tmpGroups[i];
    }

    delete [] tmpGroups;
    tmpGroups  = NULL;
}

/**
 * 
 * @param p 
 * @param groups 
 * @param n 
 */
void Tree::markGroup1(clustalw::TreeNode* p, int *groups, int n)
{
    int i;

    for (i = 0; i < n; i++)
    {
        if (olptr[i] == p)
        {
            groups[i] = 1;
        }
        else
        {
            groups[i] = 0;
        }
    }
}

/**
 * 
 * @param p 
 * @param groups 
 * @param n 
 */
void Tree::markGroup2(clustalw::TreeNode* p, int *groups, int n)
{
    int i;

    for (i = 0; i < n; i++)
    {
        if (olptr[i] == p)
        {
            groups[i] = 2;
        }
        else if (groups[i] != 0)
        {
            groups[i] = 1;
        }
    }
}

/**
 * 
 * @return 
 */
clustalw::TreeNode* Tree::avail()
{
    clustalw::TreeNode* p;
    p = new TreeNode; 
    p->left = NULL;
    p->right = NULL;
    p->parent = NULL;
    p->dist = 0.0;
    p->leaf = 0;
    p->order = 0;
    return (p);
}

/**
 * 
 * @param p 
 * @param parent 
 * @param pleaf 
 * @param pname 
 * @param pdist 
 */
void Tree::setInfo(TreeNode* p, TreeNode* parent, int pleaf, string pname, float
                     pdist)
{
    p->parent = parent;
    p->leaf = pleaf;
    p->dist = pdist;
    p->order = 0;
    p->name = pname;
    if (p->leaf == true)
    {
        p->left = NULL;
        p->right = NULL;
    }
}
          
}
