// $Id: treesum.cpp,v 1.117 2018/01/03 21:33:04 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#include <cassert>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
#include <string>

#include "local_build.h"

#include "chainmanager.h"
#include "constants.h"
#include "enable_shared_from_this.hpp"  // For shared_from_this in RecTreeSummary handling of
                                        // recombination events for partnerpicks logic.
#include "force.h"                      // For setting up the recsbypartition vector of intervals
                                        // in a RecTreeSummary.
#include "range.h"                      // For Link-related typedefs and constants.
#include "registry.h"
#include "summary.h"
#include "tree.h"
#include "treesum.h"
#include "xmlsum_strings.h"             // For xml sumfile strings.

#ifdef DMALLOC_FUNC_CHECK
#include "/usr/local/include/dmalloc.h"
#endif

// This turns on the detailed print out of the creation and analysis of each coalescence tree.
// JRM 4/10
//#define PRINT_TREE_DETAILS

// Dumps all coalesences to rdata if defined; if not, only final coalescence is written.
// JRM 4/10
//#define SAVE_ALL_COAL

using namespace std;

//------------------------------------------------------------------------------------

typedef map<string, double> Forcemap;
typedef map<string, double>::iterator Forcemapiter;

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

// This constructor is used only to make the initial TreeSummary prototype.

TreeSummary::TreeSummary()
{
    // insure that the pointers are NULL; later code
    // expects them to be NULL if they have not been set.

    m_coalsummary = NULL;
    m_migsummary = NULL;
    m_recsummary = NULL;
    m_growsummary = NULL;
    m_diseasesummary = NULL;
    m_epochsummary = NULL;
    m_divmigsummary = NULL;

} // TreeSummary ctor

//------------------------------------------------------------------------------------
// This copy constructor produces a usable EMPTY TreeSummary (no tree in it).
// No method is provided to copy the contents of a TreeSummary; it is considered too
// expensive.  (They can't be put in STL containers anyway as they are polymorphic.)

TreeSummary::TreeSummary(const TreeSummary & src)
    : m_intervalData(),
      m_nCopies(0)
{
    Sumconst_iter it = src.m_summaries.begin();
    for ( ; it != src.m_summaries.end(); ++it)
    {
        Summary * sum = it->second->Clone(m_intervalData);
        m_summaries.insert(make_pair(it->first, sum));
    }
    CacheSummaries();
} // Copy ctor

//------------------------------------------------------------------------------------

TreeSummary * TreeSummary::Clone() const
{
    return new TreeSummary(*this);
} // TreeSummary::Clone

//------------------------------------------------------------------------------------

TreeSummary::~TreeSummary()
{
    Sumiter it = m_summaries.begin();
    for ( ; it != m_summaries.end(); ++it)
    {
        delete it->second;
    }
} // dtor

//------------------------------------------------------------------------------------

void TreeSummary::Compress()
{
    Summap::iterator it = m_summaries.begin();
    bool allshort = true;

    for ( ; it != m_summaries.end(); ++it)
    {
        allshort = it->second->Compress() && allshort;
    }
    if (allshort) m_intervalData.clear();

} // Compress

//------------------------------------------------------------------------------------
// MDEBUG Currently the tree is summarized if either the tree or the stick has changed.
// This may be inefficient if tree changes are rare and stick changes are common.
// Revisit this decision if maximization slows down.

void TreeSummary::Summarize(const Tree & tree)
{
    m_nCopies = 1;

    const TimeList & timeList = tree.GetTimeList();
#ifdef PRINT_TREE_DETAILS
    cerr << "in TreeSummary::Summarize" << endl;
    timeList.PrintTreeList();           // JRM debug
    cerr << "RootTime: " << timeList.RootTime() << endl;
#endif
    // Tally up populations of tips into lineages vector.

    BranchBuffer lineages(registry.GetDataPack());
    Branchconstiter brit;
#ifdef DUMP_TREE_COAL_RDATA
    //DUMP_TREE_COAL_RDATA is defined in chainmanager.h
    //rdata is opened and closed in chainmanager.cpp
    double roottime = 0;
    for (brit = timeList.FirstBody(); brit != timeList.EndBranch();
         brit = timeList.NextBody(brit))
    {

        roottime = (*brit)->m_eventTime;

#ifdef SAVE_ALL_COAL
        rdata << (*brit)->m_eventTime << ", "; // write every coal time // JRM debug
#endif
    }

#ifndef SAVE_ALL_COAL
    rdata << roottime; // write root time // JRM debug
#endif

    rdata << endl; // end coal time line // JRM debug
#endif


    // Update the lineages.
    for (brit = timeList.FirstTip(); brit != timeList.EndBranch(); brit = timeList.NextTip(brit))
    {

        lineages.UpdateBranchCounts((*brit)->m_partitions, true);
#ifdef PRINT_TREE_DETAILS
        cerr << " updated branch count to add tip " << *brit
             << " ID: " << (*brit)->GetID()
             << " to partitions"
             << endl; // JRM print
#endif
    }

    // Traverse body of tree collecting information.

#ifdef PRINT_TREE_DETAILS
    cerr << " before scoreEvent loop" << endl;
    lineages.PrintXParts();
#endif
    double epochtime(0.0);
    for (brit = timeList.FirstBody(); brit != timeList.EndBranch();
         brit = timeList.NextBody(brit))
    {
        // Score the branch.
        Branch_ptr pBranch = *brit;

        // We do not score EpochBranches for an already encountered epoch time.
        // This call adjusts lineages as a side effect.
        if (pBranch->GetTime() != epochtime)
        {
            pBranch->ScoreEvent(*this, lineages);
        }

        if (pBranch->Event() == btypeEpoch)
        {
            if (pBranch->GetTime() == epochtime)
            {
                // No ScoreEvent for this branch; it's later in a set.
                lineages.UpdateBranchCounts(pBranch->m_partitions);
                lineages.UpdateBranchCounts(pBranch->Child(0)->m_partitions, false);
            }
            else
            {
                // This branch did ScoreEvent above; it defines the new epoch time.
                epochtime = pBranch->GetTime();
            }
        }
#ifdef PRINT_TREE_DETAILS
        cerr << " after scoreEvent call ";
        lineages.PrintXParts();
#endif

    }
#ifdef PRINT_TREE_DETAILS
    cerr << " after scoreEvent loop" << endl;
    lineages.PrintXParts();
#endif

    // now summarize the stick
    if (tree.UsingStick())
    {
        tree.ScoreStick(*this);
    }

    Compress();
    CacheSummaries(); // JCHECK--this isn't called in RecTreeSummary::Summarize

} // Summarize

//------------------------------------------------------------------------------------

#ifdef STATIONARIES

void TreeSummary::DumpStationariesData(const Tree & tree, const ForceParameters & fp) const
{
    ofstream of,migdir;
    of.open(INTERVALFILE.c_str(), ios::app);

    const TimeList & timeList = tree.GetTimeList();
    Branchconstiter brit;

    long int interval = 0;
    double topTime = 0.0;

#ifdef DUMP_TREE_COAL_RDATA
    // MDEBUG:  following code is a braindead way to do this!
    //DUMP_TREE_COAL_RDATA is defined in chainmanager.h
    //rdata is opened and closed in chainmanager.cpp
    double roottime;
    for (brit = timeList.FirstBody(); brit != timeList.EndBranch();
         brit = timeList.NextBody(brit))
    {
        roottime = (*brit)->m_eventTime;
    }
    rdata << roottime << endl; // dump root time 
#endif

    migdir.open("migdir.out", ios::app);
    double lastepochtime(0.0);

    for (brit = timeList.FirstBody(); brit != timeList.EndBranch();
         brit = timeList.NextBody(brit))
    {
        Branch_ptr pBranch = *brit;

        if (pBranch->Event() == btypeEpoch) {
           lastepochtime = pBranch->GetTime();
           continue;
        }

        double bottomTime = pBranch->m_eventTime;
        of << "int" << interval << " " << bottomTime - topTime << endl;
        ++interval;
        topTime = bottomTime;

        if (pBranch->Event() == btypeDivMig) {
           if (pBranch->GetTime() <= lastepochtime) 
              throw(1);
           migdir << "root" << pBranch->GetPartition(force_DIVMIG);
           migdir << "tip";
           migdir << pBranch->Child(0)->GetPartition(force_DIVMIG) << endl;
        }
    }
    migdir.close();

    of.close();

    interval = 0;
    topTime = 0.0;
    ofstream coalint;
    coalint.open("coalint.out", ios::app);
    for (brit = timeList.FirstCoal(); brit != timeList.EndBranch();
         brit = timeList.NextCoal(brit))
    {
        Branch_ptr pBranch = *brit;

        double bottomTime = pBranch->m_eventTime;
        coalint << "cint" << interval << " " << bottomTime - topTime;
        coalint << endl;
        ++interval;
        topTime = bottomTime;
    }
    coalint.close();

    of.open(MIGFILE.c_str(), ios::app);
    long int migs = timeList.HowMany(btypeMig);
    migs += timeList.HowMany(btypeDivMig);
    of << migs << " " << endl;
    of.close();

    of.open(DISFILE.c_str(), ios::app);
    long int ndisevents = timeList.HowMany(btypeDisease);
    of << ndisevents << " " << endl;
    of.close();

    of.open(RECFILE.c_str(), ios::app);
    long int recs = timeList.HowMany(btypeRec);
    // divide by two because HowMany gives the number
    // of branches, not events
    recs = recs / 2;
    of << recs  << " " << endl;
    of.close();

    if (fp.GetEpochTimes().size() > 0)
    {
        of.open(EPOCHFILE.c_str(), ios::app);
        vector<double> etimes(fp.GetEpochTimes());
        vector<double>::const_iterator et;
        for(et = etimes.begin(); et != etimes.end(); ++et)
           of << (*et) << " ";
        of << endl;
        of.close();
    }

} // DumpStationariesData

#endif // STATIONARIES

//------------------------------------------------------------------------------------
// This is OPTIMIZATION code which supports the specialized GetMigSummary
// and so forth functions.  It's ugly and force-specific but a significant
// speedup over calling GetSummary(lamarcstrings::MIG).

void TreeSummary::CacheSummaries()
{
    m_coalsummary = GetSummary(force_COAL);
    m_migsummary = GetSummary(force_MIG);
    m_diseasesummary = GetSummary(force_DISEASE);
    m_recsummary = GetSummary(force_REC);
    m_growsummary = GetSummary(force_GROW);
    m_epochsummary = GetSummary(force_DIVERGENCE);
    m_divmigsummary = GetSummary(force_DIVMIG);

} // CacheSummaries

//------------------------------------------------------------------------------------

map<force_type, DoubleVec1d> TreeSummary::InspectSummary() const
{
    // for each force, get a vector containing the number of events
    // for each parameter; return them in a map indexed by force

    Summap::const_iterator summary;
    map<force_type, DoubleVec1d> counts;

    for (summary = m_summaries.begin(); summary != m_summaries.end(); ++summary)
    {
        counts.insert(make_pair(summary->first, summary->second->GetShortPoint()));
    }

    return counts;
} // InspectSummary

//------------------------------------------------------------------------------------

void TreeSummary::AdjustSummary(const map<force_type, DoubleVec1d> & counts, long int region)
{
    map<force_type, DoubleVec1d>::const_iterator fit;

    for (fit = counts.begin(); fit != counts.end(); ++fit)
    {
        Summary * sum = GetSummary(fit->first);
        assert(sum);  // no summary?!
        sum->AdjustSummary(fit->second, region);
    }

} // AdjustSummary

//------------------------------------------------------------------------------------

Summary * TreeSummary::GetSummary(force_type type)
{
    // Returning NULL is not necessarily indicative of an error
    // state--some forces, notably Growth, do not have corresponding
    // Summaries.

    Sumiter it = m_summaries.find(type);
    if (it == m_summaries.end()) return NULL;
    else return it->second;

} // GetSummary

//------------------------------------------------------------------------------------

Summary const* TreeSummary::GetSummary(force_type type) const
{
    // Returning NULL is not necessarily indicative of an error
    // state--some forces, notably Growth, do not have corresponding
    // Summaries.

    Sumconst_iter it = m_summaries.find(type);
    if (it == m_summaries.end()) return NULL;
    else return it->second;

} // GetSummary const

//------------------------------------------------------------------------------------

void TreeSummary::AddSummary(force_type type, Summary * sum)
{
    // we should not be adding an already present type!
    assert(m_summaries.find(type) == m_summaries.end());

    m_summaries.insert(make_pair(type, sum));

} // AddSummary

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

RecTreeSummary::RecTreeSummary()
    : TreeSummary()
{
    const ForceVec partforces(registry.GetForceSummary().GetPartitionForces());
    ForceVec::const_iterator pforce;
    for(pforce = partforces.begin(); pforce != partforces.end(); ++pforce)
    {
        vector<Interval*> sums;
        vector<vector<Interval*> > recs((*pforce)->GetNPartitions(), sums);
        m_recsbypart.push_back(recs);
    }
    m_diseasepresent = registry.GetForceSummary().CheckForce(force_DISEASE);
} // RecTreeSummary::ctor

//------------------------------------------------------------------------------------

RecTreeSummary::RecTreeSummary(const RecTreeSummary & src)
    : TreeSummary(src),
      m_recsbypart(src.m_recsbypart)
{
    m_diseasepresent = src.m_diseasepresent;
} // RecTreeSummary::copy ctor

//------------------------------------------------------------------------------------

TreeSummary * RecTreeSummary::Clone() const
{
    return new RecTreeSummary(*this);
} // RecTreeSummary::Clone

//------------------------------------------------------------------------------------

void RecTreeSummary::Summarize(const Tree & tree)
{
    m_nCopies = 1;
    BranchBuffer lineages(registry.GetDataPack());

    const TimeList & timeList = tree.GetTimeList();

    // Tally up populations of tips into lineages vector

    Branchconstiter brit;
    Linkweight recweight(ZERO);

    for (brit = timeList.FirstTip(); brit != timeList.EndBranch(); brit = timeList.NextTip(brit))
    {
        lineages.UpdateBranchCounts((*brit)->m_partitions);
        recweight += (*brit)->GetRangePtr()->GetCurTargetLinkweight();
    }

    // Traverse tree collecting information

    bool scorethisrecbranch(true);
    double epochtime(0.0);
    for (brit = timeList.FirstBody(); brit != timeList.EndBranch(); brit = timeList.NextBody(brit))
    {
        Branch_ptr pBranch = *brit;
        // We do not score EpochBranches for an already encountered epoch time.
        if (pBranch->GetTime() != epochtime)
        {
            // ScoreEvent returns information via reference variable "recweight" here.
            pBranch->ScoreEvent(*this, lineages, recweight);
        }

        if (pBranch->Event() == btypeEpoch)
        {
            if (pBranch->GetTime() == epochtime)
            {
                // No ScoreEvent for this branch; it's later in a set.
                lineages.UpdateBranchCounts(pBranch->m_partitions);
                lineages.UpdateBranchCounts(pBranch->Child(0)->m_partitions, false);
            }
            else
            {
                // This branch did ScoreEvent above; it defines the new epoch time.
                epochtime = pBranch->GetTime();
            }
        }

        // We assume that recombination events are adjacent in the timelist.
        // MDEBUG MFIX!  Is this right?  It seems arbitrary about which branch is scored.
        // Is that justifiable in the presence of disease?
        // I don't think so!  And this code should be in RBranch::ScoreEvent to fix it.
        if (m_diseasepresent && pBranch->Event() == btypeRec)
        {
            if (scorethisrecbranch)
            {
                dynamic_cast<RecSummary *>(m_recsummary)->AddToRecombinationCounts(pBranch->m_partitions);
                AddRecToRecsByPart(pBranch->m_partitions, m_recsummary->GetLastAdded());
            }
            scorethisrecbranch = !scorethisrecbranch;
        }
    }

    // now summarize the stick
    if (tree.UsingStick())
    {
        tree.ScoreStick(*this);
    }

    Compress();

} // Summarize

//------------------------------------------------------------------------------------

void RecTreeSummary::AddRecToRecsByPart(const LongVec1d & membership, Interval* newrec)
{
    LongVec1d::size_type partforce;
    for(partforce = 0; partforce < membership.size(); ++partforce)
    {
        m_recsbypart[partforce][membership[partforce]].push_back(newrec);
    }
} // SetupRecByPartitionPointers

//------------------------------------------------------------------------------------

const vector<vector<vector<Interval*> > > & RecTreeSummary::GetRecsByPart() const
{
    return m_recsbypart;
} // GetRecsByPart

//------------------------------------------------------------------------------------
// Function: Writes tree summaries to sumfile.
// Notes:  Doesn't assume cached summary ptrs set to null for forces not
//   in use/existence since cached summary ptrs could look valid for a
//   force when the force isn't even on.  Should test for this condition if
//   going to optimize out the map lookup (call to GetSummary).

void TreeSummary::WriteTreeSummary (ofstream & out)
{
    out << "\t" << xmlsum::TREESUM_START << endl;
    out << "\t\t" << xmlsum::NCOPY_START << " " << GetNCopies()
        << " " << xmlsum::NCOPY_END << endl;

    CacheSummaries(); //just in case.

    // If growth is on, we probably have to write out full intervals,
    // not just points and waits.
    // However, some forces may only come in a short form anyway, if they never
    // happened, or only happened once (like recombination, for example).
    // But IntervalData has this information stored in it now, so we're OK.

    string form = "short";  //Either 'short' or 'long'.
    DoubleVec1d sp;
    DoubleVec1d sw;
    LongVec2d shortpick;

    vector<force_type> fvec;
    fvec.push_back(force_COAL);
    fvec.push_back(force_MIG);
    fvec.push_back(force_DISEASE);
    fvec.push_back(force_REC);
    fvec.push_back(force_DIVERGENCE);
    fvec.push_back(force_DIVMIG);

    bool somelong = false;

    for (unsigned long int fvi = 0; fvi < fvec.size(); fvi++)
    {
        Summary * currsum = GetSummary(fvec[fvi]);
        if (currsum)
        {
            if (!(currsum->GetShortness()))
            {
                form = "long";
                somelong = true;
            }
            sp = currsum->GetShortPoint();
            sw = currsum->GetShortWait();
            shortpick = currsum->GetShortPicks();

            out << "\t\t" << xmlsum::SHORTFORCE_START << " "
                << ToShortString(fvec[fvi])
                << " " << form << endl;
            WriteShorts( out, sp, sw, shortpick);
            out << "\t\t" << xmlsum::SHORTFORCE_END << endl;
        }
        form = "short";
    }

    if (somelong)
    {
        out << "\t\t" << xmlsum::INTERVALS_START << endl;
        m_intervalData.WriteIntervalData(out);
        out << "\t\t" << xmlsum::INTERVALS_END << endl;
    }
    out << "\t" << xmlsum::TREESUM_END << endl;
} // TreeSummary::WriteTreeSummary

//------------------------------------------------------------------------------------
// Writes a summary's shortwait and shortpoint to sumfile.

void TreeSummary::WriteShorts (ofstream & out, DoubleVec1d & shortpoint,
                               DoubleVec1d & shortwait, LongVec2d & shortpick) const
{
    vector<double>::iterator itstart;
    vector<double>::iterator itend;

    itstart = shortpoint.begin();
    itend = shortpoint.end();

    out << "\t\t\t" << xmlsum::SHORTPOINT_START << " ";
    for( ; itstart!= itend; ++itstart )
        out << *itstart << " ";
    out << xmlsum::SHORTPOINT_END << endl;

    itstart = shortwait.begin();
    itend = shortwait.end();

    out << "\t\t\t" << xmlsum::SHORTWAIT_START << " ";
    for( ; itstart!= itend; ++itstart )
        out << *itstart << " ";
    out << xmlsum::SHORTWAIT_END << endl;

    if (!shortpick.empty())
    {
        LongVec2d::iterator fitstart = shortpick.begin();
        LongVec2d::iterator fitend = shortpick.end();
        out << "\t\t\t" << xmlsum::SHORTPICK_START << endl;
        for( ; fitstart!= fitend; ++fitstart )
        {
            out << "\t\t\t\t" << xmlsum::SHORTPICK_FORCE_START << " ";
            LongVec1d::iterator istart = fitstart->begin();
            LongVec1d::iterator iend = fitstart->end();
            for( ; istart!= iend; ++istart )
                out << *istart << " ";
            out << xmlsum::SHORTPICK_FORCE_END << endl;
        }
        out << "\t\t\t" << xmlsum::SHORTPICK_END << endl;
    }
}

//------------------------------------------------------------------------------------
// Fills an individual tree summary with the appropriate summaries.
// precondition:  last str read in was xmlsum::TREESUM_START
// postcondition: last str read in should be xmlsum::TREESUM_END

void TreeSummary::ReadInTreeSummary ( ifstream & in )
{
    string tag;
    in >> tag;

    SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInTreeSummary",
                                          xmlsum::NCOPY_START, tag);
    long int tt;
    in >> tt;
    m_nCopies = tt;
    in >> tag;
    SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInTreeSummary",
                                          xmlsum::NCOPY_END, tag);

    in >> tag;
    force_type type;
    string typeString;
    string form;
    while ( !in.eof() && tag != xmlsum::TREESUM_END )
    {
        if ( tag == xmlsum::SHORTFORCE_START )
        {
            in >> typeString;
            type = ProduceForceTypeOrBarf(typeString);
            in >> form;
            Summary * current_sum = GetSummary(type);
            if (form=="short")
                current_sum->SetShortness(1);
            else
                current_sum->SetShortness(0);

            ReadInSummary(type, tag, in);
            in >> tag;
            SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInTreeSummary",
                                                  xmlsum::SHORTFORCE_END, tag );
        }
        else if ( tag == xmlsum::INTERVALS_START )
        {
            ReadInIntervals(tag, in);
            SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInTreeSummary",
                                                  xmlsum::INTERVALS_END, tag );
        }
        in >> tag;
    }
} // TreeSummary::ReadInTreeSummary

//------------------------------------------------------------------------------------
// Fill a vector of doubles for one force (it doesn't know which one it does).
// precondition: next string read in should be xmlsum::SHORTPOINT_START
// postcondition: last string read in should be xmlsum::SHORTWAIT_END
//                next string to be read should be an end force tag

void TreeSummary::ReadInSummary ( force_type type, string & tag, ifstream & in )
{
    Summary * current_sum = GetSummary(type);

    in >> tag;
    SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInSummary", xmlsum::SHORTPOINT_START, tag );
    in >> tag;
    while ( tag != xmlsum::SHORTPOINT_END )
    {
        current_sum->AddShortPoint( tag );
        in >> tag;
    }

    in >> tag;
    SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInSummary", xmlsum::SHORTWAIT_START, tag );
    in >> tag;
    while ( tag != xmlsum::SHORTWAIT_END )
    {
        current_sum->AddShortWait( tag );
        in >> tag;
    }

    ForceSummary & fs = registry.GetForceSummary();
    if (fs.GetNLocalPartitionForces() > 0 && fs.CheckForce(force_REC) && type == force_COAL)
    {
        // Called only when underlying data structures (trees, branches, ranges)
        // are potentially recombinant (ie, contain RecRanges, not Ranges).
        in >> tag;
        SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInSummary", xmlsum::SHORTPICK_START, tag );
        in >> tag;
        while ( tag != xmlsum::SHORTPICK_END )
        {
            SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInSummary", xmlsum::SHORTPICK_FORCE_START, tag );
            in >> tag;
            StringVec1d newpicks;
            while ( tag != xmlsum::SHORTPICK_FORCE_END)
            {
                in >> tag;
                newpicks.push_back(tag);
            }
            current_sum->AddShortPicks( newpicks );
            in >> tag;
        }
    }

} // TreeSummary::ReadInSummary

//------------------------------------------------------------------------------------

void TreeSummary::ReadInIntervals( string & tag, ifstream & in )
{
    in >> tag;
    while (tag == xmlsum::FORCE_START)
    {
        string typeString;
        force_type type;
        in >> typeString;
        type = ProduceForceTypeOrBarf(typeString);
        in >> tag;
        SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInIntervals", xmlsum::FORCE_END, tag );
        in >> tag;
        SumFileHandler::ReadInCheckFileFormat("TreeSummary::ReadInIntervals", xmlsum::ENDTIME_START, tag );
        in >> tag;
        double endtime = atof(tag.c_str());
        in >> tag;
        SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::ENDTIME_END, tag );
        in >> tag;
        long int oldstatus = 0;
        if (tag == xmlsum::OLDSTATUS_START)
        {
            in >> tag;
            oldstatus = atol(tag.c_str());
            in >> tag;
            SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::OLDSTATUS_END, tag );
            in >> tag;
        }

        long int newstatus = 0;
        if (tag == xmlsum::NEWSTATUS_START)
        {
            in >> tag;
            newstatus = atol(tag.c_str());
            in >> tag;
            SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::NEWSTATUS_END, tag );
            in >> tag;
        }

        // "recweight" is a Link recombination weight (Biglink weight or number of Littlelinks).
        Linkweight recweight(ZERO);
        if (tag == xmlsum::RECWEIGHT_START)
        {
            in >> tag;
#ifdef RUN_BIGLINKS
            recweight = atof(tag.c_str());
#else
            recweight = atol(tag.c_str());
#endif
            in >> tag;
            SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::RECWEIGHT_END, tag );
            in >> tag;
        }

        // "recpoint" is a recombination Littlelink (middle of target Biglink in Biglink version).
        long int recpoint = 0;
        if (tag == xmlsum::RECPOINT_START)
        {
            in >> tag;
            recpoint = atol(tag.c_str());
            in >> tag;
            SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::RECPOINT_END, tag );
            in >> tag;
        }

        SumFileHandler::ReadInCheckFileFormat( "TreeSummary::ReadInInterval", xmlsum::XPARTLINES_START, tag );
        in >> tag;
        LongVec1d xpartlines;
        while (tag != xmlsum::XPARTLINES_END)
        {
            xpartlines.push_back(atol(tag.c_str()));
            in >> tag;
        }

        in >> tag;
        LongVec2d partlines;

        if (tag == xmlsum::PARTLINES_START)
        {
            in >> tag;
            LongVec1d part_temp;
            while (tag != xmlsum::PARTLINES_END)
            {
                while (tag != ".")
                {
                    part_temp.push_back(atol(tag.c_str()));
                    in >> tag;
                }
                partlines.push_back(part_temp);
                part_temp.clear();
                in >> tag;
            }
            in >> tag;
        }

        LongVec1d partnerpicks;
        if (tag == xmlsum::PARTNERPICKS_START)
        {
            in >> tag;
            while (tag != xmlsum::PARTNERPICKS_END)
            {
                while (tag != ".")
                {
                    partnerpicks.push_back(atol(tag.c_str()));
                    in >> tag;
                }
                in >> tag;
            }
            in >> tag;
        }

        //The Summary function 'AddInterval' adds the interval to the interval
        // list while also setting up the appropriate pointers for that force
        // type's path through the intervals.
        Summary * sumptr = GetSummary(type);
        if (sumptr)
            sumptr->AddInterval(endtime, partlines, xpartlines, recweight,
                                oldstatus, newstatus, recpoint, partnerpicks, type);
        else
        {
            //Assume the interval is free-floating, and add the interval directly.
            Interval* fakeint = NULL;
            m_intervalData.AddInterval(fakeint, endtime, partlines, xpartlines, recweight,
                                       oldstatus, newstatus, recpoint, partnerpicks, type);
        }
    }
}

//------------------------------------------------------------------------------------

void TreeSummary::PrintIntervalData() const
{
    m_intervalData.PrintIntervalData();
}

//------------------------------------------------------------------------------------

void TreeSummary::PrintStickSummaryToFile(ofstream & of) const
{
    DoubleVec2d::size_type njoints(m_stickData.freqs.size());
    assert(m_stickData.lengths.size() == njoints);
    assert(m_stickData.lnfreqs.size() == njoints);

    // we add a cap of 50 joints to save file space...
    if (njoints > 50) njoints = 50;

    DoubleVec2d::size_type joint;
    for(joint = 0; joint < njoints; ++joint)
    {
        of << "joint#" << joint << " ";
        of << "freqA=" << m_stickData.freqs[joint][0] << " :lnfreqA=";
        of << m_stickData.lnfreqs[joint][0] << ";" << " with length=";
        of << m_stickData.lengths[joint] << endl;
    }
}

//____________________________________________________________________________________
