// $Id: chainmanager.cpp,v 1.218 2018/01/03 21:32:54 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 <cstdlib>
#include <iostream>
#include <setjmp.h>

#include "local_build.h"

#include "analyzer.h"
#include "bayesanalyzer_1d.h"
#include "cellmanager.h"
#include "chain.h"
#include "chainmanager.h"
#include "chainout.h"
#include "chainpack.h"
#include "chainparam.h"
#include "constants.h"
#include "datapack.h"
#include "definitions.h"
#include "force.h"
#include "forcesummary.h"
#include "likelihood.h"
#include "maximizer.h"
#include "maximizer_strings.h"
#include "outputfile.h"
#include "range.h"                      // For RecRange::BuildBiglinkMap and related objects.
#include "region.h"
#include "registry.h"
#include "runreport.h"
#include "stringx.h"
#include "timex.h"
#include "tree.h"
#include "treesum.h"
#include "vector_constants.h"

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

using namespace std;

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

#ifdef DUMP_TREE_COAL_RDATA
ofstream rdata;                         // JRM debug
#endif

#ifdef RUN_BIGLINKS
BiglinkVectormap RecRange::s_biglink_vectormap; // Define RecRange static variable.
#endif

// Define Range static variable.  Initialized with an "illegal" value so we can detect
// a call to a Range/RecRange constructor before it gets initialized properly.
long int Range::s_numRegionSites(FLAGLONG);

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

class RegionGammaInfo;

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
jmp_buf prewrite;
#endif

const double TEMPSWAPMIN =  0.1;        // Triggers decrease of temperature interval.
const double TEMPSWAPMAX = 0.4;         // Triggers increase of temperature interval.
const double TEMPINCR = 1.1;            // Proportion to increase temperature.
const double TEMPDECR = 0.9;            // Proportion to decrease temperature.
const double MAXTEMP  = 1000;           // The maximum temperature we'll allow.
const long int ADJUSTINTERVAL = 20;     // About how many swaps between adaptive temperature changes.
const double MAXDIFF = 100.0;           // Maximum difference between temperatures.
const double MINDIFF = 0.001;           // Minimum difference between temperatures.

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

ChainManager::ChainManager(RunReport& runrep, Maximizer& maximizer)
    : m_chainparam(registry.GetChainParameters()),
      m_runreport(runrep),
      m_randomsource(registry.GetRandom()),
      m_maximizer(maximizer),
      m_bayesanalyzer(registry.GetBayesAnalyzer_1D()),
      m_chainpack(),
      m_nregions(registry.GetDataPack().GetNRegions()),
      m_nreplicates(m_chainparam.GetNReps()),
      m_collectionmanager(m_nregions, m_chainparam.GetNRepsAndNProfileReps(registry.GetForceSummary().GetAllNParameters())),
      m_sumfilehandler(),
      m_totalsteps(0), m_currentsteps(0),
      m_logGeyerWeights(),
      m_writesumfile(false), m_readsumfile(false),
      m_recoversumfile(false),
      m_recover_region(0), m_recover_replicate(0), m_recover_chaintype(0),
      m_recover_chain(0),
      m_recover_redochain(false),
      m_recover_redomaximization(false),
      m_redoreplicatesum(false),
      m_redoregionsum(false)
{
    long int chtype;
    for(chtype = 0; chtype < NCHAINTYPES; ++chtype)
    {
        long int steps = m_chainparam.GetNSamples(chtype) * m_chainparam.GetInterval(chtype) + m_chainparam.GetNDiscard(chtype);
        m_nsteps.push_back(steps);
        m_totalsteps += steps * m_nreplicates * m_chainparam.GetNChains(chtype);

        // added profile replicate chains are of type "long int"
        if (registry.GetChainParameters().RunProfileReps() && chtype == 1)
        {
            m_totalsteps += steps *
                registry.GetChainParameters().GetNProfileReps(registry.GetForceSummary().GetAllNParameters());
        }
    }

    m_multitemp = (m_chainparam.GetAllTemperatures().size() > 1);
    if (m_multitemp)
    {
        long int chunk;
        for(chtype = 0; chtype < NCHAINTYPES; ++chtype)
        {
            long int interval = m_chainparam.GetTempInterval();
            LongVec1d chunks;
            if (m_nsteps[chtype] < interval)
                chunks.push_back(m_nsteps[chtype]);
            else
            {
                for(chunk = interval; chunk <= m_nsteps[chtype]; chunk += interval)
                {
                    chunks.push_back(interval);
                    if (chunk != m_nsteps[chtype] && chunk+interval > m_nsteps[chtype])
                        chunks.push_back(m_nsteps[chtype]-chunk);
                }
            }
            m_chunksize.push_back(chunks);
        }
    }
    else
    {
        for(chtype = 0; chtype < NCHAINTYPES; ++chtype)
        {
            LongVec1d chunks(1,m_nsteps[chtype]);
            m_chunksize.push_back(chunks);
        }
    }

    vector<pair<ForceParameters, long int> > emptypar;
    vector<pair<DoubleVec1d, long int> > emptystick;

} // ChainManager::ChainManager

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

ChainManager::~ChainManager()
{
    // intentionally blank
} // ChainManager::~ChainManager

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

void ChainManager::DoAllChains()
{
    // note -- moved writing of xml file into lamarc.cpp
#ifdef DUMP_TREE_COAL_RDATA
    // save final tree coalescence times for R to display
    rdata.open("rdata",ios::app);       // JRM debug
#endif

    // chainmanager obj seems to be created before user exits menu
    m_readsumfile         = registry.GetUserParameters().GetReadSumFile();
    m_writesumfile        = registry.GetUserParameters().GetWriteSumFile();
    if (m_readsumfile)                  // Open and read in the input summary file.
    {
        long int total_chains = 0;
        for (int i = 0; i < NCHAINTYPES; i++ )
        {
            total_chains += registry.GetChainParameters().GetNChains(i);
        }
        m_sumfilehandler.ReadInSumFile(m_chainpack, m_collectionmanager, total_chains);
        ReadInRecover();

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
        if (setjmp (prewrite))
        {
            CloseSumOut();
        }
#endif

        if (m_writesumfile)
        {
            m_writesumfile = false;
            // We'll reset it in DoChainFromSummaryFile(), but for now, it's much
            //  more efficient to write them all at once, above, than whilst going
            //  through the DoRegions/DoReplicates/DoChainTypes fandango.
            m_sumfilehandler.WriteSumFileStart();
            m_sumfilehandler.WriteWhatWasRead(m_recoversumfile,
                                              m_recover_region,
                                              m_recover_replicate,
                                              m_recover_chaintype,
                                              m_recover_chain,
                                              m_recover_redochain,
                                              m_recover_redomaximization,
                                              m_nregions,
                                              m_nreplicates,
                                              m_chainpack,
                                              m_collectionmanager);
        }
    }

    else if(m_writesumfile)             //Open and start writing to the output summary file.
    {
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
        if (setjmp (prewrite))
        {
            CloseSumOut();
        }
        else
        {
            m_sumfilehandler.WriteSumFileStart();
        }
#else
        m_sumfilehandler.WriteSumFileStart();
#endif

        //This is an 'else if' in case the user is reading and writing to the
        // same file--we want to read in the data before clobbering it.
    }

    CreateChains();
    DoRegions();

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
    if (setjmp (prewrite))
    {
        CloseSumOut();
    }
#endif

    if (m_writesumfile)
        m_sumfilehandler.WriteSumFileEnd(m_chainpack);

#ifndef STATIONARIES
#ifndef LAMARC_QA_SINGLE_DENOVOS
    // For LAMARC_QA_SINGLE_DENOVOS, we don't want to
    // combine any MLE/profile data over regions.

    if (m_chainparam.IsBayesian())
        m_bayesanalyzer.CalcProfiles(FLAGLONG);
    else
        DoOverallProfiles();

    try                                 // JDEBUG--this variable will be created during menu/xml reading
    {                                   // and stored in the UserParam--eventually...
        ResultsFile results;
        results.Display();
    }

    catch (file_error& e)
    {
        throw e;
    }

#endif // LAMARC_QA_SINGLE_DENOVOS
#endif // STATIONARIES

#ifdef DUMP_TREE_COAL_RDATA
    rdata.close(); // JRM debug
#endif

} // ChainManager::DoAllChains

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

//The basic scheme of ReadInRecover is to go through the information in
// chainpack and check to see what's been set and what hasn't, and to check
// the collectionmanager to make sure all the tree summaries are accurately
// stored there, too.  When it reaches something that hasn't been set,
// LAMARC then knows it needs to re-calculate those values, and sets various
// m_recover_* variables appropriately.  These are then read by the main
// program as it goes through its DoRegions/DoReplicates/DoChainTypes/DoChain
// dance, and are set to trigger normal code execution at the appropriate
// juncture.  WriteWhatWasRead also uses these variables when writing a new
// summary file that is intended to match the old one.  The various flags
// and counters are as follows:
//
//   bool m_recoversumfile:  True if the summary file was partial, false if not.
//   long m_recover_region:  The index of the region where the sumfile stopped,
//                           or (m_nregions-1) if all regions trees were read.
//   long m_recover_replicate:  The index of the replicate where the sumfiles
//                              stopped, or (m_nreplicates-1) if all replicate
//                              trees were read.
//   long m_recover_chaintype:  The index of the chain type where the sumfiles
//                              stopped, or (NCHAINTYPES-1) if all trees read.
//   long m_recover_chain:  The index of the chain where the sumfiles stopped.
//                          Note that this is the chain *for the particular
//                          chaintype* and not the absolute chain.  Set to
//                          registry.GetChainParameters().GetNChains(m_recover_chaintype)
//                          if all trees were read.  Note also that this is
//                          not the standard '-1' that all the other values
//                          get--it is not a valid index, merely an indication
//                          that we're actually done and don't need to
//                          redo this chain.
//   bool m_redoreplicatesum:  True if the summary over all replicates of the
//                             last recorded region was not recorded in the
//                             sumfile, but the tree summaries were.
//   bool m_redoregionsum:  True if the summary over all regions was not
//                          recorded in the sumfile, but the tree summaries
//                          were.

void ChainManager::ReadInRecover()
{
    long int total_chains = 0;
    for (int i = 0; i < NCHAINTYPES; i++ )
    {
        total_chains += registry.GetChainParameters().GetNChains(i);
    }

    m_recover_region = m_sumfilehandler.GetLastRegion();
    m_recover_replicate = m_sumfilehandler.GetLastReplicate();

    long int last_chain = m_sumfilehandler.GetLastChain();
    SetRecoverChaintypeChainsFrom(last_chain);

    long int regionChainSum = m_sumfilehandler.GetLastRegionChainSum();
    long int replicateChainSum = m_sumfilehandler.GetLastReplicateChainSum();

    if (replicateChainSum > m_recover_replicate)
    {
        //There must be only one chain per replicate, and we wrote out the
        // trees for a replicate but not the chainpack.
        assert(total_chains == 1);
        m_recover_region = regionChainSum;
        m_recover_replicate = replicateChainSum;
        m_recover_chain = 0;
        m_recover_redomaximization = true;
        m_chainpack.EndReplicate();
    }
    else if (regionChainSum > m_recover_region)
    {
        //There must be only one chain per region, and we wrote out the trees
        // but not the chainpack.
        assert(total_chains == 1);
        m_recover_region = regionChainSum;
        m_recover_chain = 0;
        m_recover_redomaximization = true;
        m_chainpack.EndRegion();
    }
    else if (last_chain+1 == total_chains)
    {
        //There are three possible omissions here--the final chain summary,
        // the final replicate summary, and the final region summary.
        if ((regionChainSum != m_recover_region) || (replicateChainSum != m_recover_replicate))
        {
            //No final chainsum.
            m_recover_redochain = true;
            m_recover_chain--;
        }
        else if ((m_sumfilehandler.GetLastReplicateSummary()-1 != m_recover_region)
                 && (m_recover_replicate == m_nreplicates-1)
                 && (m_nreplicates > 1))
        {
            //No summary over replicates for this region.
            m_redoreplicatesum = true;
        }
        else if ((!m_sumfilehandler.GetRegionSummary()) &&
                 (m_recover_region == m_nregions-1) &&
                 (m_recover_replicate == m_nreplicates-1) &&
                 (m_nregions > 1))
        {
            //No summary over regions.
            m_redoregionsum = true;
        }
        else
        {
            //We have everything--advance to the next replicate and/or region
            if (m_recover_replicate+1 == m_nreplicates)
            {
                //On to the next region
                m_chainpack.EndRegion();
                m_recover_region++;
                m_recover_replicate = 0;
            }
            else
            {
                //On to the next replicate
                m_chainpack.EndReplicate();
                m_recover_replicate++;
            }
            m_recover_chaintype = 0;
            m_recover_chain = 0;
        }
    }
    else if (last_chain+2 == total_chains)
    {
        //Check to see if we have a chain summary but no chainpack--in this case,
        // we need to recalculate the maximum on that last chain.
        if ((regionChainSum == m_recover_region) && (replicateChainSum == m_recover_replicate))
        {
            //We have a final chainsum, but no final chainpack
            m_recover_redomaximization = true;
            m_recover_chain = registry.GetChainParameters().GetNChains(NCHAINTYPES-1)-1;
        }
    }

    m_recoversumfile = !m_sumfilehandler.GetRegionSummary();
    //We'll assume that if the region summary is there, everything else was OK,
    // since that's the last thing that gets written.

    TellUserWhereWeAreRestarting();
} // ReadInRecover

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

//Sets the member variables m_recover_chaintype and m_recover_chain from a
// number that equals the index of the final chain read in.

void ChainManager::SetRecoverChaintypeChainsFrom(long int last_chain)
{
    long int read_chains = last_chain+1;
    //We want the index of the chain *after* the last read chain.
    long int earlier_chains = 0;

    for (int i = 0; i < NCHAINTYPES; i++)
    {
        long int these_chains = registry.GetChainParameters().GetNChains(i);
        if (read_chains < earlier_chains + these_chains)
        {
            m_recover_chaintype = i;
            m_recover_chain = read_chains - earlier_chains;
            return;
        }
        earlier_chains += these_chains;
    }

    //If we get here, we've read the last chain of the list, so in case
    // we still need to do things at this point, set the chaintype/chain
    // to the last one.
    m_recover_chaintype = NCHAINTYPES-1;
    m_recover_chain = registry.GetChainParameters().GetNChains(m_recover_chaintype);
}

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

void ChainManager::TellUserWhereWeAreRestarting()
{
    string msg;
    if (m_recover_region >= m_nregions)
    {
        //Everything was intact
        msg = "The summary file was complete, so no new chains need to be created."
            "  Any requested profiling will be calculated at the appropriate place"
            " in the run.";
    }
    else if (m_redoregionsum)
    {
        msg = "The summary file was mostly complete, with the exception of the "
            "calculation of the final summary over all regions.  This will be re-"
            "calculated, along with any requested profiling.";
    }
    else if (m_redoreplicatesum)
    {
        msg = "The summary file did not include the summary over replicates for"
            " region " + ToString(m_recover_region+1) + ", so new calculations will"
            " begin at that point, along with any requested profiling before then.";
    }
    else
    {
        msg = "The summary file was incomplete.  After calculating any requested profiles from summarized replicates, chains will resume being calculated from ";
        if (m_recover_redochain)
        {
            msg += "the last chain of region "
                + ToString(m_recover_region + 1) + ", replicate "
                + ToString(m_recover_replicate + 1) + ", re-creating the final chain, "
                "since the summary of that chain was lost.";
        }
        else if (m_recover_redomaximization)
        {
            msg += "the last chain of region "
                + ToString(m_recover_region + 1) + ", replicate "
                + ToString(m_recover_replicate + 1) + ", re-calculating the parameter "
                "estimates from the data stored in the summary file.";
        }
        else
        {
            msg += "region " + ToString(m_recover_region + 1)
                + ", replicate "  + ToString(m_recover_replicate + 1)
                + ", chain type " + ToString(m_recover_chaintype + 1)
                + ", and chain "  + ToString(m_recover_chain + 1) + ".";
        }
    }
    m_runreport.ReportNormal(msg);
}

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

void ChainManager::CreateChains()
{
    DoubleVec1d temperatures = m_chainparam.GetAllTemperatures();
    DoubleVec1d::iterator temp;

    for(temp = temperatures.begin(); temp != temperatures.end(); ++temp)
    {
        Chain ch(m_randomsource,m_runreport,m_chainparam,m_collectionmanager,*temp);
        m_temps.push_back(ch);
    }
} // ChainManager::CreateChains

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

void ChainManager::DoRegions()
{
    vector<Chain>::iterator temp;
    long int region;

#ifndef STATIONARIES
    ForceSummary & forcesum = registry.GetForceSummary();
    Analyzer & analyzer = registry.GetAnalyzer();
#endif // STATIONARIES

    for(region = 0; region < m_nregions; ++region)
    {
        // Record region number. used for outputting recombination locations
        registry.SetCurrentRegionIndex(region);

        // Clear the DLCell store, since it can generally not be reused between regions.
        registry.GetCellManager().ClearStore();
        m_currentsteps = 0;

        Region& curregion = registry.GetDataPack().GetRegion(region);

#ifdef ENABLE_REGION_DUMP
        // All user input is done, and all processing for the current region is about to start.
        // Print the parsed input data relevant to analysis of this Region.
        PrintRegionData(region, curregion);
#endif  // ENABLE_REGION_DUMP

        // Set the total number of sites for this Region.  This includes sites in all Loci in the Region plus all
        // non-marker inter-locus sites (sites for which we have no data).  Recombination can happen at any link
        // between any of these sites, and therefore we must account for all this "space" in both Biglink and emulated
        // Littlelink models.  They are already treated as recombination-possible links in the Littlelink model.
        Range::SetNumRegionSites(curregion);

#ifdef RUN_BIGLINKS
        // If using Biglink optimization, build the map that associates Littlelinks with Biglinks.
        if (registry.GetForceSummary().CheckForce(force_REC)) // This force includes recombination.
        {
            // Called only when underlying data structures (trees, branches, ranges) will be recombinant.
            RecRange::BuildBiglinkMap(curregion);
        }
#endif  // RUN_BIGLINKS

        for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
        {
            Tree* regtr = curregion.CreateTree();
            temp->StartRegion(regtr);
            m_regiontrees.push_back(regtr);
        }

        if (registry.GetUserParameters().GetProgress() != NONE)
        {
            m_runreport.ReportUrgent("", false);
            string msg = "Beginning region: ";
            msg += curregion.GetRegionName();
            m_runreport.ReportUrgent(msg);
        }

        DoReplicates(region);
        registry.GetUserParameters().ClearCurrentBestLike();

        vector<Tree*>::iterator tree = m_regiontrees.begin();
        for(temp = m_temps.begin(); temp != m_temps.end(); ++temp, ++tree)
        {
            temp->EndRegion();
            delete *tree;
        }

        m_regiontrees.clear();

        if (!m_readsumfile)
        {
            m_chainpack.EndRegion();
        }

#ifndef STATIONARIES
#ifndef LAMARC_QA_SINGLE_DENOVOS
        // For LAMARC_QA_SINGLE_DENOVOS, we don't want to
        // combine any MLE/profile data over replicates.
        if (m_chainparam.IsBayesian())
        {
            m_bayesanalyzer.CalcProfiles(region);
        }
        else
        {
            if (forcesum.GetOverallProfileType() != profile_NONE)
            {
                ForceParameters region_fp = m_chainpack.GetRegion(region).GetEstimates();
                DoubleVec1d region_MLEs = region_fp.GetRegionalParameters();
                double region_like = m_chainpack.GetRegion(region).GetLlikemle();
                analyzer.CalcProfiles(region_MLEs, region_like, region);
            }
        }
        ChainOut chout = m_chainpack.GetRegion(region);
        forcesum.SetRegionMLEs(chout, region);
        m_runreport.PrognoseAll(m_chainpack, region, m_nregions);
#endif // LAMARC_QA_SINGLE_DENOVOS
#endif // STATIONARIES
    }

#ifdef RUN_BIGLINKS
    if (registry.GetForceSummary().CheckForce(force_REC)) // This force includes recombination.
    {
        // Called only when underlying data structures (trees, branches, ranges)
        // are potentially recombinant (ie, contain RecRanges, not Ranges).
        // We are done with the Biglink Vector Map; we can clear it now.
        RecRange::GetBiglinkVectormap().clear();
    }
#endif  // RUN_BIGLINKS

    if (m_redoregionsum)
    {
        m_readsumfile = false;
        m_writesumfile = registry.GetUserParameters().GetWriteSumFile();
        m_runreport.ReportChat("Recalculating the overall region summary.\n");
    }

    registry.GetCellManager().ClearStore(); // static function called to clear datalikehood
    // memory manager of final region

#ifndef STATIONARIES
    if (m_chainparam.IsBayesian())
    {
        m_bayesanalyzer.EndRegionsAndAnalyze();
    }

    if (m_nregions > 1)
    {
        m_runreport.ReportNormal("Calculating the best parameter estimates over all regions.");
        CalculateMLEsOverRegions();
    }
#endif // STATIONARIES
} // ChainManager::DoRegions()

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

void ChainManager::CalculateMLEsOverRegions()
{
    ForceSummary& forcesum = registry.GetForceSummary();
    m_maximizer.SetLikelihood(&(registry.GetRegionPostLike()));
    RegionGammaInfo *pRegionGammaInfo = registry.GetRegionGammaInfo();
    double maxlike;
    ForceParameters fp(global_region);
    ChainOut regionout;
    string msg;

    if (pRegionGammaInfo)
    {
        if (m_chainparam.IsBayesian())
            throw implementation_error("ChainManager::DoRegions(), can\'t apply a gamma over regions within a Bayesian analysis.");
        pRegionGammaInfo->Activate();
        // Here we need to get the constraint matrix from FS.
        // Note: we can use its dimensionality to deterine the argument for Initialize().
        m_maximizer.Initialize(forcesum.GetAllNParameters() + 1);
        m_maximizer.SetConstraints(forcesum.GetIdenticalGroupedParams());
        m_maximizer.SetLikelihood(&(registry.GetRegionPostLike()));
        m_maximizer.AppendConstraintOnAlpha(pRegionGammaInfo->GetParamStatus());
    }

    if (m_chainparam.IsBayesian())
    {
#if 0  // EWFIX.BUG.838 -- moved to curvefiles.cpp, called from main Lamarc routine
        if (registry.GetUserParameters().GetWriteCurveFiles())
        {
            m_bayesanalyzer.WriteCurvesForRegion(FLAGLONG);
        }
#endif // 0

        DoubleVec1d mleparam = m_bayesanalyzer.GetMaxVecForAllRegions();
        maxlike = m_bayesanalyzer.GetAvgMaxLikeForAllRegions();

        fp.SetGlobalParameters(mleparam);
        regionout.SetEstimates(fp);
        regionout.SetLlikemle(maxlike);
    }
    else
    {                                   // Non-Bayesian
        registry.GetRegionPostLike().Setup(m_collectionmanager.GetTreeColl(),
                                           m_logGeyerWeights);
        CalculateNonBayesMultiRegionMLEs(fp, regionout, maxlike);
    }

    // Note:  even if we read in estimates from a summary file, we still
    //  use the new values instead of the old ones.
    if (m_readsumfile)
    {
        CompareAndWarn(regionout.GetEstimates(), m_chainpack.GetOverall().GetEstimates());
    }

    m_chainpack.SetSummaryOverRegions(regionout);
    if (pRegionGammaInfo)
        pRegionGammaInfo->Deactivate();
    forcesum.SetOverallMLE(regionout);
    if (pRegionGammaInfo)
        pRegionGammaInfo->Activate();

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
    if (setjmp (prewrite))
    {
        CloseSumOut();
    }
#endif

    if (m_writesumfile)
    {
        m_sumfilehandler.WriteRegionSummary(fp, maxlike);
    }

    m_runreport.ReportNormal("", false);
    m_runreport.ReportNormal("Final parameter estimates using data from all regions:", false);
    m_runreport.DisplayReport(regionout);

    //LS DEBUG:  This is a hack.  It should go away when the gamma force leaves the registry.
    if (pRegionGammaInfo)
    {
        string msg = "Alpha: " + ToString(pRegionGammaInfo->GetMLE());
        if (pRegionGammaInfo->GetParamStatus().Status() == pstat_constant)
        {
            msg += " (held constant)";
        }
        m_runreport.ReportUrgent(msg);
    }
}

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

void ChainManager::CalculateNonBayesMultiRegionMLEs(ForceParameters& fp, ChainOut& regionout, double& maxlike)
{
    RegionGammaInfo *pRegionGammaInfo = registry.GetRegionGammaInfo();
    string msg;

    DoubleVec1d params = m_chainpack.OverallMeanParams();

    if (pRegionGammaInfo)
    {
        params.push_back(pRegionGammaInfo->GetStartValue());
    }

    bool retval(false), atLeastOneSearchSucceeded(false);
    double oneHighAlpha = FLAGDOUBLE;
    string message = "";
    DoubleVec1d MLEs(params.size() +1+1+1); // plus lnL, region, retval
    DoubleVec2d peaks; // peak(s) in the multi-region lnL surface
    // found by searching from different starting points

    // We have found (November 2004) that adding the single-region
    // likelihood surfaces together can yield a multi-region likelihood
    // surface with multiple local peaks.  Hence, we search this composite
    // surface multiple times, starting from a different point each time.

    // First, try starting from the mean of the single-region MLE vectors.
    retval = m_maximizer.Calculate(params, maxlike, message);
    if (message == maxstr::MAX_HIGH_ALPHA_0)
    {
        oneHighAlpha = params[params.size()-1];
    }
    else if (message != "")
    {
        message = "Warning:  When calculating the maximum over all regions starting from the mean"
            " of the single-region estimates, we received the following warning from the maximizer:  "
            + message;
        m_runreport.ReportDebug(message);
    }

    message = "";
    // param was set to mean, above
    // Store these results in a temporary vector.
    MLEs[0] = maxlike;
    MLEs[1] = -1.0; // signifies the mean of single-region MLE vectors
    MLEs[2] = retval ? 1.0 : 0.0;
    if (retval)
    {
        atLeastOneSearchSucceeded = true; // found a true (local) maximum
    }
    copy(params.begin(), params.end(), &MLEs[3]);
    peaks.push_back(MLEs);

    // Next, try starting from each single-region peak.
    for (long int region = 0; region < m_nregions; region++)
    {
        params = m_chainpack.GetRegion(region).GetEstimates().GetGlobalParameters();
        if (pRegionGammaInfo)
        {
            params.push_back(pRegionGammaInfo->GetStartValue());
        }
        retval = m_maximizer.Calculate(params, maxlike, message);
        if (message == maxstr::MAX_HIGH_ALPHA_0)
        {
            oneHighAlpha = params[params.size()-1];
        }
        else if (message != "")
        {
            message = "Warning:  when calculating the maximum over all regions starting from the the region "
                + ToString(region) + " estimates, we received the following warning from the maximizer:  "
                + message;
            m_runreport.ReportDebug(message);
        }
        message = "";
        MLEs[0] = maxlike;
        MLEs[1] = static_cast<double>(region);
        MLEs[2] = retval ? 1.0 : 0.0;
        if (retval)
            atLeastOneSearchSucceeded = true;
        copy(params.begin(), params.end(), &MLEs[3]);
        peaks.push_back(MLEs);
    }

    unsigned long int IndexOfFirstReasonableResult(0);
    if (atLeastOneSearchSucceeded)
    {
        // From the peak(s) we found, determine the highest one;
        // report on the others when we're in debug mode.
        sort(peaks.begin(), peaks.end(), greater<DoubleVec1d>());
        while (IndexOfFirstReasonableResult < peaks.size() &&
               peaks[IndexOfFirstReasonableResult][0] > DBL_BIG/10.0)
            IndexOfFirstReasonableResult++;

        msg = "Multi-region maximum-likelihood parameter estimates:\n";
        for (unsigned long int j = 0; j < peaks.size(); j++)
        {
            if (1.0 == peaks[j][2]) // maximizer succeeded
            {
                msg += "lnL = " + Pretty(peaks[j][0]) + ", p = ("
                    + Pretty(peaks[j][3]);
                for (unsigned long int k = 4; k < peaks[j].size(); k++)
                    msg +=  ", " + Pretty(peaks[j][k]);
                msg += "), starting from ";
            }
            else
                msg += "Failed to find a maximum when starting from ";
            if (-1.0 == peaks[j][1])
                msg += "the mean over single-region peaks.\n";
            else
                msg += "region " + ToString(peaks[j][1]) + "\'s peak.\n";
        }
        if (peaks.size() == IndexOfFirstReasonableResult)
            IndexOfFirstReasonableResult = 0; // no reasonable result found; default to 0
        maxlike = peaks[IndexOfFirstReasonableResult][0]; // the highest lnL we found
        if (maxlike > DBL_MAX)
            maxlike = DBL_MAX;
        copy(&peaks[IndexOfFirstReasonableResult][3],
             &(peaks[IndexOfFirstReasonableResult][peaks[IndexOfFirstReasonableResult].size()]),
             params.begin());
    }
    else if (oneHighAlpha != FLAGDOUBLE)
    {
        //All our searches failed, but at least one failed due to having a high
        // alpha.  Try again, constraining alpha at its maximum.
        pRegionGammaInfo->ConstrainToMax();
        m_maximizer.AppendConstraintOnAlpha(pRegionGammaInfo->GetParamStatus());
        msg = maxstr::MAX_BAD_ALPHA_0
            + ToString(pRegionGammaInfo->GetMaxValue())
            + maxstr::MAX_BAD_ALPHA_1;
        registry.GetRunReport().ReportNormal(msg);
        CalculateNonBayesMultiRegionMLEs(fp, regionout, maxlike);
        return;
    }
    else
    {
        msg = maxstr::MAX_NO_MULTI_MAX;
        maxlike = -DBL_BIG;
        for (unsigned long int n = 0; n < peaks.size(); n++)
        {
            // Find the entry corresponding to the mean
            // of the single-region MLEs, and propagate this.
            if (-1.0 == peaks[n][1])
            {
                for (unsigned long int i = 3; i < peaks[n].size(); i++)
                    params[i - 3] = peaks[n][i];
            }
            break;
        }
    }

    m_runreport.ReportDebug(msg);

    if (pRegionGammaInfo)
    {
        pRegionGammaInfo->SetMLE(params[params.size()-1]);
        params.pop_back(); // remove alpha from this vector
    }

    fp.SetGlobalParameters(params);
    regionout.SetEstimates(fp);
    regionout.SetLlikemle(maxlike);
}

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

void ChainManager::DoReplicates(long int region)
{
    const ForceSummary& forcesum = registry.GetForceSummary();
    long int rep;

    for(rep = 0; rep < m_nreplicates; ++rep)
    {
        registry.GetUserParameters().UpdateFileNamesAndSteps(region, rep, m_readsumfile);
        if (!m_readsumfile)
        {
            ResetAllAlphas();
        }

        vector<Chain>::iterator temp;
        Region& curregion = registry.GetDataPack().GetRegion(region);
        if (m_nreplicates > 1)
        {
            string msg = "Beginning Replicate ";
            msg += indexToKey(rep) + " of " + curregion.GetRegionName() + ".";
            m_runreport.ReportUrgent(msg);
        }

        for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
        {
            temp->StartReplicate(forcesum, curregion);
        }

        m_maximizer.SetLikelihood(&(registry.GetSinglePostLike()));
        DoChainTypes(region, rep);

        for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
            temp->EndReplicate();

        if (!m_readsumfile)
        {
            m_chainpack.EndReplicate();
        }
    }

    registry.GetDataPack().GetRegion(region).WriteAnyMapping();
#ifndef STATIONARIES
    if (m_chainparam.IsBayesian())
    {
        m_bayesanalyzer.EndReplicatesAndAnalyze();

#if 0  // EWFIX.BUG.838 -- moved to curvefiles.cpp, called from main Lamarc routine
        if (registry.GetUserParameters().GetWriteCurveFiles())
            m_bayesanalyzer.WriteCurvesForRegion(region);
#endif // 0

    }
#endif // STATIONARIES

    if (m_redoreplicatesum && (m_recover_region==region))
    {
        m_readsumfile = false;
        m_writesumfile = registry.GetUserParameters().GetWriteSumFile();
        m_runreport.ReportChat("Re-calculating the region summary over replicates.\n");
    }

    if (m_readsumfile && !m_chainparam.IsBayesian())
    {
        RedoMaximization(region);
        //ReadInNoRedoMax(region);
        //LS NOTE:  uncomment ReadInNoRedoMax and comment out RedoMaximization for
        // faster reads from summary files with no checking.
    }

    if (m_nreplicates > 1)
    {
        if (!m_readsumfile)
        {
            m_runreport.ReportNormal("Calculating the best parameter estimates over all replicates.");
            if (!m_chainparam.IsBayesian())
            {
                m_maximizer.SetLikelihood(&(registry.GetReplicatePostLike()));
                registry.GetReplicatePostLike().Setup(m_collectionmanager.GetTreeColl(region));
                SaveGeyerWeights(registry.GetReplicatePostLike().GetGeyerWeights(), region);
            }

            double maxlike;
            ForceParameters fp(region);
            ChainOut repout = ChainOut();

            if (!m_chainparam.IsBayesian())
            {
                DoubleVec1d meanparam = m_chainpack.RegionalMeanParams();
                string message = "";
                if (!m_maximizer.Calculate(meanparam, maxlike, message))
                {
                    string msg = "Maximization failure when calculating the best ";
                    msg = msg + "parameters for all replicates in region " +
                        registry.GetDataPack().GetRegion(region).GetRegionName() +
                        ".  Using the mean values for all replicates instead.";
                    m_runreport.ReportUrgent(msg);
                    m_runreport.ReportNormal("Error from the maximizer:  " + message);
                    //Note:  This relies on the maximizer leaving 'meanparam' unchanged
                    // during failure.
                }
                else if (message != "")
                {
                    m_runreport.ReportNormal("Warning from the maximizer:  " + message);
                }
                fp.SetRegionalParameters(meanparam);
                repout.SetEstimates(fp);
                repout.SetLlikemle(maxlike);
            }
            else
            {
                DoubleVec1d mleparam = m_bayesanalyzer.GetMaxVecForRegion(region);
                maxlike = m_bayesanalyzer.GetAvgMaxLikeForRegion(region);
                fp.SetGlobalParameters(mleparam);
                repout.SetEstimates(fp);
                repout.SetLlikemle(maxlike);
            }

            m_chainpack.SetSummaryOverReps(repout);

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
            if (setjmp (prewrite))
            {
                CloseSumOut();
            }
#endif

            if (m_writesumfile)
            {
                m_sumfilehandler.WriteReplicateSummary(fp, maxlike, m_chainpack);
            }

            m_runreport.ReportNormal("", false);
            m_runreport.ReportNormal("Parameter estimates using data from all replicates in this region:", false);
            m_runreport.DisplayReport(repout);

            //Save the mapping data to the appropriate loci, then report on it.
            if (registry.GetDataPack().GetRegion(region).GetNumMovingLoci() > 0)
            {
                DoubleVec1d logweights(m_nreplicates, 0.0);
                if (!m_chainparam.IsBayesian())
                {
                    logweights = m_logGeyerWeights[region];
                }
                registry.GetDataPack().GetRegion(region).SaveMappingInfo(m_collectionmanager.GetMapColl(region), logweights);
                registry.GetDataPack().GetRegion(region).ReportMappingInfo();
            }
        }
    }
} // ChainManager::DoReplicates

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

void ChainManager::DoChainTypes(long int region, long int rep)
{
    const ForceSummary& forcesum = registry.GetForceSummary();
    ForceParameters chainstart(forcesum.GetStartParameters(),region);

    for(long int chaintype = 0; chaintype < NCHAINTYPES; ++chaintype)
    {
        vector<Chain>::iterator temp;
        for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
            temp->SetChainType(chaintype, m_chainparam);
        for(long int chain = 0; chain < m_chainparam.GetNChains(chaintype); ++chain)
        {
            if (m_readsumfile)
            {
                DoChainFromSummaryFile(region, rep, chaintype, chain);
            }
            else
            {
                DoChain(region, rep, chaintype, chain, chainstart);
            }
        }
    }

#ifndef STATIONARIES
    // In a stationaries run we do not attempt any post-analysis
    if (m_chainparam.IsBayesian())
    {
        if (m_readsumfile)
        {
            ChainOut co;
            ForceParameters fp(region);
            DoSingleChainBayes(region, rep, co, fp);
        }
        m_bayesanalyzer.EndChainsAndAnalyze();
    }
#endif // STATIONARIES
} // ChainManager::DoChainTypes

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

// DoChainFromSummaryFile does absolutely nothing unless it's time to
//  re-start a run from where the summary file broke off.  Its trigger
//  is set in ReadInRecover() in the form of several 'recover*' member
//  variables.  When the code here is finally triggered, it runs DoChain
//  twice; both times with the force parameter values it got from the
//  last read-in chain (stored in the chainpack).  The first time is just
//  to get better-than-de-novo trees into the various chains; the parameters
//  determined from that are thrown away.  The second time is 'for real',
//  and the member boolean 'm_readsumfile' is turned off so from then on,
//  DoChain is called instead of this function.

void ChainManager::DoChainFromSummaryFile(long int region, long int rep, long int chaintype, long int chain)
{
    if (m_recoversumfile
        && (region==m_recover_region) && (rep == m_recover_replicate)
        && (chaintype==m_recover_chaintype) && (chain==m_recover_chain))
    {
        m_readsumfile  = false; //we're done reading.
        registry.GetUserParameters().UpdateWriteTraceFile(region, rep);
        if (chaintype == 0 && chain == 0)
        {
            //We've either got nothing at all in the insumfile, or we're starting
            // up immediately after a region/replicate.  In either case, we only
            // want to run DoChain, not anything as fancy as below.
            m_writesumfile = registry.GetUserParameters().GetWriteSumFile();
            ForceParameters chainstart(registry.GetForceSummary().GetStartParameters(),region);
            DoChain(region, rep, chaintype, chain, chainstart);
        }
        else
        {
            ChainOut chout = m_chainpack.GetLastChain();
            ForceParameters chainstart = chout.GetEstimates();
            assert (chainstart.GetParamSpace() == known_region);

            if (m_recover_redomaximization)
            {
                //We need to only do maximization, not any creating of trees.  This
                // involves creating a chainout into which we put the resulting
                // estimates.
                m_runreport.ReportNormal("Calculating the best parameters from the stored summaries:");
                chout = ChainOut();
                if (chain == 0)
                {
                    chainstart = ForceParameters(registry.GetForceSummary().GetStartParameters(),region);
                }
                if (!m_chainparam.IsBayesian())
                {
                    DoSingleChainPosterior(region, rep, chaintype, chain, chout, chainstart);
                }
                else
                {
                    DoSingleChainBayes(region, rep, chout, chainstart);
                }

                //The following things are unknown in the current setup, but if we
                // write out the current tree somewhere, we might store this info
                // there, too.  --LS NOTE

#if 0
                chout.SetLlikedata(coldchain.GetCurrentDataLlike());
                chout.SetSwaprates(swaprates);
                chout.SetTemperatures(averagetemps);
#endif // 0

                m_chainpack.SetChain(chout);
                m_writesumfile = registry.GetUserParameters().GetWriteSumFile();

                // We just set a new chain in the chainpack--write it to a file if needed.
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
                if (setjmp (prewrite))
                {
                    CloseSumOut();
                }
#endif

                if (m_writesumfile)
                {
                    m_sumfilehandler.WriteLastChain(m_chainpack);
                }
                m_runreport.PrognoseRegion(m_chainpack, region, m_currentsteps, m_totalsteps);
                m_runreport.DisplayReport(chout);
            }
            else
            {
                //We're picking up in the middle of a run.
                if (m_recover_redochain)
                {
                    m_chainpack.RemoveLastChain();
                }
                // We need to delete the last set of summary info from chainpack,
                //  but not before we stick the force parameters into chainstart.

                //First, run a chain of chaintype 0 to try to get decent trees.
                m_runreport.ReportNormal("Re-running a single Initial Chain to generate a good start tree:");
                vector<Chain>::iterator temp;
                for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
                {
                    temp->SetChainType(0,m_chainparam);
                }
                //For this DoChain, we need to set 'm_readsumfile' since otherwise
                // we will run the 'OptimizeDataModels' routine.  Which is currently
                // MixedKS only, but oh well.  Woo special-case code!  -LS
                m_readsumfile = true;
                DoChain(region, rep, 0, 0, chainstart);
                m_readsumfile = false;

                //Now throw away the chain summary in chainpack, and reset the parameters.
                // (This is why we saved chout, above.)
                m_chainpack.RemoveLastChain();
                chainstart = chout.GetEstimates();

                // Now do the chain we wanted to do in the first place.
                //  (But with better trees.)
                m_runreport.ReportNormal("And now picking up where the summary file left off:");
                m_writesumfile = registry.GetUserParameters().GetWriteSumFile();
                // We might want to start writing again.  If so, the file should
                //  already be open.
                for(temp = m_temps.begin(); temp != m_temps.end(); ++temp)
                    temp->SetChainType(chaintype,m_chainparam);
                DoChain(region, rep, chaintype, chain, chainstart);
            }
        }
    }
    else
    {
        //Save the chainout report for output.
        long int chainnum = chain;
        string name = "Initial";
        if (chaintype == 1)
        {
            chainnum += registry.GetChainParameters().GetNChains(0);
            name = "Final";
        }
        assert(chaintype < 2); //Above code relies on there only being 2 types.
        name += " Chain " + ToString(chain+1) + ":";
        m_runreport.SaveOutput(name, false);
        ChainOut chout = m_chainpack.GetChain(region, rep, chainnum);
        m_runreport.MakeReport(chout);
        StringVec1d chainreport = m_runreport.FormatReport(chout, false, 78);
        for (unsigned long int line = 0; line < chainreport.size(); ++line)
        {
            m_runreport.SaveOutput(chainreport[line], false);
        }
    }
} // DoChainFromSummaryFile

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

// DoChain used to loop over all chains for a particular chaintype
//  itself, but the loop has been moved to DoChainTypes to make it
//  easier to pick up in the middle of a run.  In any event, this
//  is the heart of the program.  It creates a chain of trees, then
//  maximizes the force parameters over that chain.

void ChainManager::DoChain(long int region, long int rep, long int chaintype, long int chain, ForceParameters & chainstart)
{
    // We can assume that we are generating, not reading, trees here.
    // However, we have m_readsumfile set when we are re-doing initial chain
    // one, so that we can not run OptimizeDataModels.
    //  assert(!m_readsumfile);

    volatile bool lastchain = ((chaintype ==  NCHAINTYPES-1 ||
                                (chaintype ==  NCHAINTYPES-2 && registry.GetChainParameters().GetNChains(NCHAINTYPES-1) == 0)) &&
                               chain == registry.GetChainParameters().GetNChains(chaintype)-1);

    m_collectionmanager.StartChain(region, rep, lastchain);
    if (lastchain)
    {
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
        if (setjmp (prewrite))
        {
            CloseSumOut();
        }
#endif

        //LS NOTE:  We jump here if we run out of space in the middle of writing
        // other stuff--have to set things earlier.
        if (m_writesumfile)
            m_sumfilehandler.WriteChainSumStart(region, rep, m_collectionmanager);
    }

    const ForceSummary& forcesum = registry.GetForceSummary();

    bool adapt = m_chainparam.GetTempAdapt();

    unsigned long int numtemps = m_temps.size();
    DoubleVec1d averagetemps;
    double nadapts(0.0);

    for(unsigned long int temp = 0; temp < numtemps; ++temp)
    {
        // clear stored swap-success stats
        m_temps[temp].ClearTotalSwaps();
        if (m_chainparam.IsBayesian())
            m_temps[temp].StartBayesianChain(chain,chaintype,forcesum);
        else m_temps[temp].StartChain(chain,chaintype,forcesum,chainstart);
    }

    if (adapt) averagetemps.assign(numtemps,0.0);
    else averagetemps = m_chainparam.GetAllTemperatures();

    // Only attempt to "groom" the trees for chains beyond Initial Chain 1.
    // This is not done in bayesian runs, as they do not suffer from
    // changing parameter values out from under a tree at chain start.
    if (!(0 == chain && 0 == chaintype) && !m_chainparam.IsBayesian())
    {
        GroomTrees(chainstart);
    }

    unsigned long int chunk;
    for(chunk = 0; chunk < m_chunksize[chaintype].size(); ++chunk)
    {
        long int steps = m_chunksize[chaintype][chunk];
        for(unsigned long int temp = 0; temp < m_temps.size(); ++temp)
            m_temps[temp].DoOneChain(steps,lastchain);

        if (m_multitemp)                // propose a swap
        {
            unsigned long int chain1, chain2;
            ChooseTwoAdjacentChains(chain1, chain2);
            m_temps[chain1].SwapTemperatureIdentities(m_temps[chain2]);
            if (adapt) AdjustTemperatures(averagetemps, chunk, nadapts);
        }
        m_currentsteps += steps;
    }

    if (adapt)
    {
        // find the mean temperature of each chain
        // if we never tried an adaptive swap, then set to (presumably unchanged)
        //    starting temperatures
        if (nadapts)
        {
            transform(averagetemps.begin(), averagetemps.end(),
                      averagetemps.begin(), bind2nd(divides<double>(), nadapts));
        }
        else averagetemps = m_chainparam.GetAllTemperatures();
    }

    // Postprocessing
    // Only the cold chain gets postprocessed
    unsigned long int cold = FindColdChain(m_temps);
    Chain& coldchain = m_temps[cold];
    ChainOut chout = coldchain.EndChain();
    chout.SetNumtemps(numtemps);
    if (!lastchain)
    {
        // adjust the summaries, unless this is the last final chain
        m_collectionmanager.CorrectForFatalAttraction(region);
        //Need to send the region number because recombination might be
        // illegal for some regions.
    }

#ifndef STATIONARIES
    if (!lastchain && !m_readsumfile)
    {
        // Optimize the data models.  In practice, this means setting the alpha
        // for any MixedKS models, and doing nothing for anything else.
        OptimizeDataModels(region);
    }
#endif // STATIONARIES

    if (lastchain)
    {
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
        if (setjmp (prewrite))
        {
            CloseSumOut();
        }
#endif

        if (m_writesumfile)
            m_sumfilehandler.WriteChainSumEnd(m_collectionmanager);
    }

    if (!m_chainparam.IsBayesian())
        m_collectionmanager.
            GetTreeColl(region, rep)->SetStartParameters(chainstart);

#ifndef STATIONARIES

    if (!m_chainparam.IsBayesian())
    {
        DoSingleChainPosterior(region, rep, chaintype, chain, chout, chainstart);
    }
    else
    {
        DoSingleChainBayes(region, rep, chout, chainstart);
    }

    chout.SetLlikedata(coldchain.GetCurrentDataLlike());

    // Compute swap-success statistics.
    DoubleVec1d swaprates(m_temps.size());
    vector<pair<double, long int> > sortedtemps =
        SortChainsByTemperature(m_temps);

    for (unsigned long int temp = 0; temp < sortedtemps.size(); ++temp)
    {
        swaprates[temp] = m_temps[sortedtemps[temp].second].GetTotalSwapRate();
    }
    chout.SetSwaprates(swaprates);
    chout.SetTemperatures(averagetemps);
    m_chainpack.SetChain(chout);

    // We just set a new chain in the chainpack--write it to a file if needed.
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
    if (setjmp (prewrite))
    {
        CloseSumOut();
    }
#endif

    if (m_writesumfile)
    {
        m_sumfilehandler.WriteLastChain(m_chainpack);
    }

#ifndef LAMARC_QA_SINGLE_DENOVOS
    // no reporting here -- too nauseating in a tight loop
    m_runreport.PrognoseRegion(m_chainpack, region, m_currentsteps, m_totalsteps);
    m_runreport.DisplayReport(chout);

    // Save the mapping data to the appropriate loci, then report on it.
    if ((lastchain
         && registry.GetDataPack().GetRegion(region).GetNumMovingLoci() >0)
        || registry.GetDataPack().GetRegion(region).AnyJumpingAnalyses())
    {
        registry.GetDataPack().GetRegion(region).SaveMappingInfo(m_collectionmanager.GetMapColl(region, rep));
        registry.GetDataPack().GetRegion(region).ReportMappingInfo();
    }
#endif // LAMARC_QA_SINGLE_DENOVOS
#endif // STATIONARIES

} // ChainManager::DoChain

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

void ChainManager::GroomTrees(ForceParameters& chainstart)
{
    // Because the growth values that yield the maximum likelihood for the trees
    // of the previous chain can be dramatically different from the growth values
    // that generated that chain, especially in the presence of migration, a
    // problem can arise.  For each temperature, including the cold temperature,
    // its next chain will be generated using the new MLE parameters as desired,
    // but each chain will commence with a tree topology that is consistent with
    // the _old_ parameters that were used to generate the previous chain.  This
    // is especially prone to occurring in the heated chains, in which case these
    // "extreme" trees (e.g., trees which favor slow or negative growth) can get
    // swapped into the cold chain and sampled.  Such trees that are extreme for
    // the current parameters (recall these  parameters will be the starting
    // values for maximization) will lead to overflow and/or inconsistencies
    // between the likelihood and its derivatives during maximization, in which
    // case maximization will fail at the end of the next chain.  The phenotype of
    // the "extreme tree" is always, or almost always, a pair of populations
    // dwindling down to one lineage each (k=1), spending a long time interval
    // with one lineage each, then accepting a migration into the faster-growing
    // population (k=2 and k=0), followed soon, but not soon enough for the
    // current growth values, by a coalescence.
    // Hence, we attempt to retain the topology of each temperature's tree as
    // we prepare to use each to generate a new chain, but if necessary, we shrink
    // the length of a potentially-fatal time interval to a length that is
    // consistent with the expectation value of that interval under the current
    // parameter values.  This appears to solve the problem (Nov. 2004).
    // Because the timelist stores time stamps for the start and ending of each
    // interval, instead of the length of the interval, we can encounter cases
    // in which the new expectation value impels us to shrink the interval so
    // small that adding it to "starttime" to get "endtime" yields a result that
    // is indistinguishable from "starttime," effectively producing a new interval
    // of zero length.  Zero-length intervals are fatal, so in this case we
    // copy the cold tree into the offending hot tree.  If the problem is in the
    // cold tree itself, then we give up, warn the user, and assume that the
    // next maximization will fail, with the program execution path proceeding
    // from there.

    const vector<double>& growths = chainstart.GetGrowthRates();
    const vector<double>& logSelVector = chainstart.GetLogisticSelectionCoefficient();
    unsigned long int cold = FindColdChain(m_temps);
    bool positiveGrowth = false, logisticSelection = false;
    for (unsigned long int i = 0; i < growths.size(); i++)
        if (growths[i] > 0.0)
            positiveGrowth = true;
    if (!logSelVector.empty())
    {
        if (!growths.empty())
        {
            string msg = "Attempted to infer growth and logistic selection ";
            msg += "simultaneously; currently we can not co-estimate these forces.";
            throw implementation_error(msg);
        }
        logisticSelection = true;
    }

    //Only groom trees in the presence of positive growth
    //or logistic selection.
    if (!positiveGrowth && !logisticSelection)
    {
        return;
    }

    const vector<double>& thetas = chainstart.GetRegionalThetas();
    double s = (logisticSelection ? logSelVector[0] : 0.0);

    if (positiveGrowth)
    {
        for (unsigned long int temp = 0; temp < m_temps.size(); temp++)
        {
            // Tree::Groom() shrinks intervals when necessary and possible.
            // If something goes really horribly wrong inside, it will throw.
            if (!m_regiontrees[temp]->GroomForGrowth(thetas, growths,
                                                     m_temps[temp].GetTemperature()))
            {
                if (temp != cold)
                {
                    m_regiontrees[temp]->CopyTips(m_regiontrees[cold]);
                    m_regiontrees[temp]->CopyBody(m_regiontrees[cold]);
                }
                else
                {
                    string msg;
                    msg += "\nWarning!  Detected an \"extreme\" cold ";
                    msg += " tree.  It is likely that maximization will fail ";
                    msg += "for the next chain.\n";
                    registry.GetRunReport().ReportDebug(msg);
                    continue;
                }
            }

            // Whether we change an interval in the tree or replace it
            // with a copy of the cold tree, we need to ensure the
            // "oldtree" copy in chainstate is updated accordingly.
            m_temps[temp].SetChainStateOldTree(m_regiontrees[temp]);
        }
    }
    else // logistic selection
    {
        for (unsigned long int temp = 0; temp < m_temps.size(); temp++)
        {
            if (!m_regiontrees[temp]->GroomForLogisticSelection(thetas, s,
                                                                m_temps[temp].GetTemperature()))
            {
                if (temp != cold)
                {
                    m_regiontrees[temp]->CopyTips(m_regiontrees[cold]);
                    m_regiontrees[temp]->CopyBody(m_regiontrees[cold]);
                }
                else
                {
                    string msg;
                    msg += "\nWarning!  Detected an \"extreme\" cold ";
                    msg += " tree.  It is likely that maximization will fail ";
                    msg += "for the next chain.\n";
                    registry.GetRunReport().ReportDebug(msg);
                    continue;
                }
            }

            // Whether we change an interval in the tree or replace it
            // with a copy of the cold tree, we need to ensure the
            // "oldtree" copy in chainstate is updated accordingly.
            m_temps[temp].SetChainStateOldTree(m_regiontrees[temp]);
        }
    }
}

//------------------------------------------------------------------------------------
// Note:  This method is actually independent of class ChainManager,
// so it could be removed from the class.

unsigned long int ChainManager::FindColdChain(const vector<Chain>& chains) const
{
    unsigned long int chain;
    for(chain = 0; chain < chains.size(); ++chain)
    {
        if (chains[chain].IsCold())
        {
            return chain;
        }
    }
    assert(false);  // there must be a cold chain!
    return 0;
} // FindColdChain

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

// Adjusts the temperatures for the adaptive heating scheme.
// This scheme assumes that the chains are swapped between
// adjacent pairs.
// If swapping rate is less than TEMPSWAPMIN, the temperature
// difference is multiplied by TEMPDECR (reducing it).  If it
// is greater than TEMPSWAPMAX, the temperature difference is
// multiplied by TEMPINCR (increasing it).
// PB 2002

void ChainManager::AdjustTemperatures(DoubleVec1d& averagetemps, long int whichswap, double & howoften)
{
    // we will adjust temperature when there has been opportunity for
    // approximately ADJUSTINTERVAL, currently 20, swaps
    // between each temperature pair.
    long int trigger = (ADJUSTINTERVAL*(m_temps.size() - 1));

    if(whichswap != 0 && whichswap % trigger == 0)
    {
        // The chains are not, and cannot easily be, in correct sorted
        // order (due to swapping) so we determine the sorted order here.
        // averagetemps will be set in sorted order, while the chains
        // themselves will be set in their native order.

        // averagetemps is kept in sorted order for use in runtime
        // reporting and output, since the unsorted chains would be confusing.

        vector<pair<double, long int> > sorted = SortChainsByTemperature(m_temps);

        // delta holds differences between adjacent temperatures
        assert(!sorted.empty());
        DoubleVec1d delta(sorted.size()-1);
        unsigned long int i;
        for(i = 0; i < sorted.size()-1; ++i)
        {
            delta[i] = sorted[i+1].first - sorted[i].first;
        }
        // Adjust the temperature differences
        for(i = 0; i < sorted.size()-1; i++)
        {
            double swaprate = m_temps[sorted[i].second].GetSwapRate();
            // if we never tried any swaps, we don't adjust the temperatures
            if (swaprate >= 0.0)
            {
                if(swaprate <= TEMPSWAPMIN)
                {
                    // don't change them if they are already very similar
                    /* if (delta[i]*TEMPDECR > MINDIFF) */ delta[i] *= TEMPDECR;
                }
                else
                    if (swaprate > TEMPSWAPMAX)
                    {
                        // don't change them if they are already very dissimilar
                        /* if (delta[i]*TEMPINCR < MAXDIFF) */ delta[i] *= TEMPINCR;
                    }
            }
        }
        //Set swap counter to zero, do not mix this with the
        // swap counter for reporting, which is TotalSwaps().
        for(i = 0; i < sorted.size(); i++)
        {
            m_temps[sorted[i].second].ClearSwaps();
        }
        // Reset the temperatures according to the adjusted temp diffs
        if (sorted[0].first != 1)
        {
            string msg = "Error:  the lowest temperature is "
                + ToString(sorted[0].first)
                + ", which is not 1.0  Other temperatures are: ";
            for (unsigned long int i = 1; i < sorted.size(); ++i)
                msg += ToString(sorted[i].first) + "  ";
            msg  += ".\n";
            throw data_error(msg);
        }
        averagetemps[0] += 1;
        for (i = 1; i < sorted.size(); ++i)
        {
            double newtemp = sorted[i-1].first + delta[i-1];
            if (newtemp > MAXTEMP)
                newtemp = MAXTEMP;
            sorted[i].first = newtemp;
            averagetemps[i] += newtemp;
            m_temps[sorted[i].second].SetTemperature(newtemp);
        }
        ++howoften;
    }
} // AdjustTemperatures

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

void ChainManager::ChooseTwoAdjacentChains(unsigned long int & chain1, unsigned long int & chain2)
{
    // This routine must sort the chains by temperature to find out which are adjacent!
    vector <pair<double, long int> > orderedtemps = SortChainsByTemperature(m_temps);

    unsigned long int numchains = m_temps.size();
    chain1 = m_randomsource.Long(numchains - 1);
    chain2 = chain1 + 1;

    // return the index associated with each choice
    chain1 = orderedtemps[chain1].second;
    chain2 = orderedtemps[chain2].second;
} // ChooseTwoAdjacentChains

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

vector<pair<double, long int> > ChainManager::SortChainsByTemperature(const vector<Chain>& temps)
{
    vector <pair<double, long int> > orderedtemps;
    unsigned long int i;
    for (i = 0; i < temps.size(); ++i)
    {
        orderedtemps.push_back(make_pair<double, long int>(temps[i].GetTemperature(), i));
    }
    sort(orderedtemps.begin(), orderedtemps.end());
    return orderedtemps;

} // SortChainsByTemperature

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

void ChainManager::DoSingleChainPosterior(long int region, long int rep, long int chaintype,
                                          long int chain, ChainOut & chout,
                                          ForceParameters & chainstart)
{
    registry.GetSinglePostLike().Setup(m_collectionmanager.GetTreeColl(region, rep));
    DoubleVec1d mleparam = chainstart.GetRegionalParameters();
    double maxlike = 0.0;
    string message = "";

    bool maximizerCalculateOK = m_maximizer.Calculate(mleparam, maxlike, message);

    if (!maximizerCalculateOK)
    {
        m_runreport.ReportUrgent("Warning:  maximization failure for this chain.  Using the parameter estimates from the previous chain and continuing.");
        if (message != "")
        {
            m_runreport.ReportNormal("Error from the maximizer:  " + message);
        }
        else
        {
            m_runreport.ReportDebug("Whoops!  m_maximizer.Calculate() failed, but did not set a message telling us why it failed.  This needs to be fixed.");
        }
    }
    else if (message != "")
    {
        m_runreport.ReportNormal("Warning from the maximizer:  " + message);
    }

#ifndef LAMARC_QA_SINGLE_DENOVOS
    // don't want to update starting parameter values if we're testing
    // denovo generation -- we want to use the start values from the
    // user again and again

    chainstart.SetRegionalParameters(mleparam);
    // constrain the parameters, unless this is the last final chain
    if (chaintype < 1 || chain != m_chainparam.GetNChains(chaintype) - 1)
    {
        if (registry.GetForceSummary().ConstrainParameterValues(chainstart))
        {
            //The parameters were constrained.
            m_maximizer.ProfileGuideFixAll();
            mleparam = chainstart.GetRegionalParameters();
            m_maximizer.Calculate(mleparam, maxlike, message);
            //Here we're just setting maxlike; the return value and any message is ignored.
            m_maximizer.ProfileGuideRestore();
        }
    }

    chout.SetLlikemle(maxlike);
    chout.SetEstimates(chainstart);

#else // LAMARC_QA_SINGLE_DENOVOS
    // when LAMARC_QA_SINGLE_DENOVOS is defined, print out all
    // parameter values to the SINGLE_DENOVO_FILE
    // see config/local_build.h for more info

    ofstream denovoFile;
    denovoFile.open(SINGLE_DENOVO_FILE.c_str(),ios::app);

    const ParamVector pvec(true); // Ugh! This is so we can know which

    if(maximizerCalculateOK)
    {
        for(size_t mleindex=0; mleindex < mleparam.size(); mleindex++)
        {
            if(pvec[mleindex].IsValidParameter())
            {
                if(mleindex != 0)
                {
                    denovoFile << "\t";
                }
                denovoFile << mleparam[mleindex];
            }

        }
    }
    else
    {
        for(size_t mleindex=0; mleindex < mleparam.size(); mleindex++)
        {
            if(pvec[mleindex].IsValidParameter())
            {
                if(mleindex != 0)
                {
                    denovoFile << "\t";
                }
                denovoFile << "-";
            }

        }
        registry.AddDenovoMaxRejectCount(1);
    }


    if(registry.GetForceSummary().CheckForce(force_MIG) ||
       registry.GetForceSummary().CheckForce(force_DIVMIG))
    {
        const Tree & tree = *(m_regiontrees[0]);
#if 0
        const TimeList & timeList = tree.GetTimeList();
        long int migs = timeList.HowMany(btypeMig);
        migs += timeList.HowMany(btypeDivMig);
        denovoFile << "\t" << migs;
#endif

        deque<bool> migsToPrint = registry.GetMigsToPrint();
        TreeSummary * trsum = tree.SummarizeTree();
        const vector<double>& nmig = trsum->GetMigSummary()->GetShortPoint();
        size_t vv = nmig.size();
        for(size_t index = 0; index < vv; index++)
        {
            if(migsToPrint[index])
            {
                denovoFile << "\t" << nmig[index];
            }
        }

    }

    if(registry.GetForceSummary().CheckForce(force_REC))
    {

        const Tree & tree = *(m_regiontrees[0]);
        const TimeList & timeList = tree.GetTimeList();
        long int recs = timeList.HowMany(btypeRec);
        // divide by two because HowMany gives the number
        // of branches, not events
        recs = recs / 2;
        denovoFile << "\t" << recs;
    }

    denovoFile << endl;
    denovoFile.close();

#endif // LAMARC_QA_SINGLE_DENOVOS

} // DoSingleChainPosterior

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

void ChainManager::DoSingleChainBayes(long int region, long int rep, ChainOut & chout, ForceParameters & chainstart)
{
    const ParamSumm& paramsumm(m_collectionmanager.GetParamColl(region, rep)->GetParamSumm());

    m_bayesanalyzer.ReplaceLastChainAndAnalyze(paramsumm);

    //m_bayesanalyzer.AnalyzeAndAdd(paramsumm); //If we're adding up all chains.
    DoubleVec1d mleparam = m_bayesanalyzer.GetMaxVecForLastChain();
    chainstart.SetGlobalParameters(mleparam);

    chout.SetEstimates(chainstart);
    chout.SetLlikemle(m_bayesanalyzer.GetAvgMaxLikeForLastChain());
    chout.SetBayesUnique(m_bayesanalyzer.GetNumUniquePointsVec());

} // DoSingleChainBayes

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

//ReadInNoRedoMax replaces what DoSingleChainPosterior would have
// done in the initial run in that the MLEs stored in the maximizer, the
// analyzer, and the last cold chain are set to their read-in values.  The
// crucial bit seems to be the maximizer, but it can't hurt to set the other
// values as well.
//
//Note:  resurrected this function to speed up our running from summary
// files.  Don't release with this called; play it safe with RedoMaximization
// being called instead.  -LS, 12/14/05

void ChainManager::ReadInNoRedoMax (long int region)
{
    m_runreport.ReportUrgent("Warning!  Not redoing maximization over this region!"
                             "  If you see this message in a release version of LAMARC,"
                             " this function has been called in error:  please let us know"
                             " at lamarc@gs.washington.edu.\n");

    ForceParameters read_fp = m_chainpack.GetRegion(region).GetEstimates();
    DoubleVec1d read_MLEs = read_fp.GetRegionalParameters();
    ChainOut read_co = m_chainpack.GetRegion(region);
    double read_maxlike = read_co.GetLlikemle();

    if (m_nreplicates > 1)
    {
        registry.GetReplicatePostLike().Setup(m_collectionmanager.GetTreeColl(region));
        SaveGeyerWeights(registry.GetReplicatePostLike().GetGeyerWeights(), region);
        m_maximizer.SetLikelihood(&(registry.GetReplicatePostLike()));
    }
    else
    {
        registry.GetSinglePostLike().Setup(m_collectionmanager.GetTreeColl(region, 0));
        m_maximizer.SetLikelihood(&(registry.GetSinglePostLike()));
    }

    if (!m_chainparam.IsBayesian())
    {
        ForceParameters chainstart(read_fp,region);
        unsigned long int cold = FindColdChain(m_temps);
        Chain& coldchain = m_temps[cold];
        ChainOut chout = coldchain.EndChain();
        chout.SetNumtemps(m_temps.size());
        chout.SetLlikemle(read_maxlike);
        chout.SetEstimates(chainstart);

        registry.GetAnalyzer().SetMLEs(read_MLEs);
        m_maximizer.SetMLEs(read_MLEs);
    }
    else
    {
        m_runreport.ReportUrgent("Sumfile reading not implemented for Bayesian analysis yet.  Please bother your local implementors to change this.");
        assert(false);
    }
} // ReadInNoRedoMax

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

//RedoMaximization is used to check to make sure the read-in
// MLEs can be successfully re-calculated using the starting parameters of
// the run instead of the parameters from the next-to-the-last chain.

void ChainManager::RedoMaximization(long int region)
{
    string msg = "Re-calculating best parameter values.  ";
    msg += "Differences may indicate a true maximum was not found, or that "
        "an older version of LAMARC was used to create the original summary file."
        "\n";
    m_runreport.ReportChat(msg);

    if (m_nreplicates > 1)
    {
        registry.GetReplicatePostLike().Setup(m_collectionmanager.GetTreeColl(region));
        SaveGeyerWeights(registry.GetReplicatePostLike().GetGeyerWeights(), region);
        m_maximizer.SetLikelihood(&(registry.GetReplicatePostLike()));
    }
    else
    {
        registry.GetSinglePostLike().Setup(m_collectionmanager.GetTreeColl(region, 0));
        m_maximizer.SetLikelihood(&(registry.GetSinglePostLike()));
    }

    ForceSummary&   forcesum      = registry.GetForceSummary();
    ForceParameters chainstart(forcesum.GetStartParameters(), region);
    DoubleVec1d     mleparam      = chainstart.GetRegionalParameters();
    //This bit can be used to re-run the maximizer with the same values
    // it used the first time.
    long int chain = 0;
    for (int i = 0; i < NCHAINTYPES; i++ )
    {
        chain += registry.GetChainParameters().GetNChains(i);
    }
    chain = chain-2; //-1 for number->index, -1 for next-to-last instead of last.
    DoubleVec1d setup_MLEs;
    if (chain == -1)
    {
        //There is only one chain total--the next-to-last parameter input is the
        // default input, not that created from the last chain.
        setup_MLEs = mleparam;
    }
    else
    {
        ChainOut co = m_chainpack.GetRegion(region);
        setup_MLEs = co.GetEstimates().GetRegionalParameters();
    }
    double maxlike;
    string message = "";
    if (!m_maximizer.Calculate(setup_MLEs, maxlike, message))
    {
        string msg = "Maximization failed when re-calculating the estimates ";
        msg = msg + "for this data set.  If that's what happened the last time, "
            + "at least it's consistent.  The resulting estimates were obtained "
            + "from the last successfully-maximized chain.";
        m_runreport.ReportUrgent(msg);
        m_runreport.ReportNormal("Error from the maximizer:  " + message);
    }
    else if (message != "")
    {
        m_runreport.ReportNormal("Warning from the maximizer:  " + message);
    }

    ChainOut read_co = m_chainpack.GetRegion(region);
    Region& curregion = registry.GetDataPack().GetRegion(region);

    chainstart.SetRegionalParameters(setup_MLEs);
    ForceParameters readfp = m_chainpack.GetRegion(region).GetEstimates();
    CompareAndWarn(chainstart, readfp);

    double read_maxlike = read_co.GetLlikemle();
    double difference = fabs(read_maxlike - maxlike);
    double percdiff = fabs(difference/read_maxlike);
    if (percdiff > .001)
    {
        msg = "Warning:  the maximizer was unable to find the same maximum ";
        msg += "likelihood estimates as before for region \"" +
            curregion.GetRegionName() +
            "\".  The parameter values it found have a log likelihood of " +
            ToString(maxlike) + ", which is significantly different from the" +
            " maximum likelihood of the old set of parameters, " +
            ToString(read_maxlike) + " (A difference of " + ToString(difference) +
            ").  If you were running from older summary files with replication, " +
            "this is to be expected.  Otherwise, we recommend you " +
            "re-run this data set with any or all of a) longer run times, b) " +
            "more replicates, or c) more heating (adaptive or fixed).\n";
        m_runreport.ReportUrgent(msg);
    }
    else if (difference > pow(10.0, -(SUMFILE_PRECISION-7)) )
    {
        msg = "The newly-calculated maximum likelihood for ";
        msg += "region \"" + curregion.GetRegionName() + "\" (" + ToString(maxlike)
            + ") is different from the maximum likelihood as read in from "
            + "the summary file (" + ToString(read_maxlike) + ").  "
            + "(A difference of " + ToString(difference)
            + ", which exceeds the recommended minimum difference of "
            + ToString(pow(10.0, -(SUMFILE_PRECISION-7))) + ".)\n";
        m_runreport.ReportNormal(msg);
    }

    //We want to use the newly-calculated values, not the read in ones.  This
    // means changing the ones in the chainpack.
    ChainOut co;
    if (m_nreplicates > 1)
    {
        co = m_chainpack.GetRegion(region);
    }
    else
    {
        co = m_chainpack.GetLastChain(region);
    }
    co.SetEstimates(chainstart);
    co.SetLlikemle(maxlike);
    if (m_nreplicates > 1)
    {
        m_chainpack.ResetSummaryOverReps(co, region);
    }
    else
    {
        m_chainpack.ResetLastChain(co, region);
    }
    forcesum.SetRegionMLEs(co, region);
    //Just to make sure, we set stuff in the analyzer and maximizer, too.  I
    // don't *think* we need to, but just in case.
    registry.GetAnalyzer().SetMLEs(chainstart.GetRegionalParameters());
    m_maximizer.SetMLEs(chainstart.GetRegionalParameters());
} // RedoMaximization

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

void ChainManager::CompareAndWarn(ForceParameters calcfp, ForceParameters readfp)
{
    vector<force_type> fvec;
    // MDEBUG needs updating whenever a Force is added, which is not ideal
    //  NB: the following are in no particular order!
    fvec.push_back(force_COAL);
    fvec.push_back(force_MIG);
    fvec.push_back(force_DISEASE);
    fvec.push_back(force_REC);
    fvec.push_back(force_GROW);
    fvec.push_back(force_LOGISTICSELECTION);
    fvec.push_back(force_DIVMIG);
    fvec.push_back(force_DIVERGENCE);

    for (unsigned long int fvi = 0; fvi < fvec.size(); fvi++)
    {
        DoubleVec1d calcnums = calcfp.GetGlobalParametersByTag(fvec[fvi]);
        DoubleVec1d readnums = readfp.GetGlobalParametersByTag(fvec[fvi]);
        if (calcnums.size() != readnums.size())
        {
            string msg = "The ";
            msg += ToString(fvec[fvi]) + " forces for the read-in values differ in "
                + "number from the calculated values, which probably means the read-in "
                + "data differs from that used to write out the data.  It is strongly "
                + "recommended that you exit the program now and correct this.\n";
            m_runreport.ReportUrgent(msg);
        }
        else
        {
            for (unsigned long int numi = 0; numi<calcnums.size(); ++numi)
            {
                double difference = fabs(calcnums[numi] - readnums[numi]);
                double percdiff = difference/readnums[numi];
                if (percdiff > .001)
                {
                    //pow(10.0, -(SUMFILE_PRECISION-6))) {
                    string msg = "Warning:  A newly-calculated value for ";
                    msg += "force \"" + ToString(fvec[fvi]) + "\" ("
                        + ToString(calcnums[numi])
                        + ") is different from that read in from "
                        + "the summary file (" + ToString(readnums[numi]) + ").  "
                        + "(A difference of " + ToString(difference)
                        + ", which exceeds the recommended minimum difference of 0.1%).\n";
                    m_runreport.ReportUrgent(msg);
                }
                else if (calcnums[numi] != 0)
                {
                    string msg = "The " + ToString(fvec[fvi]) + " force estimate "
                        + ToString(calcnums[numi])
                        + " was accurately re-calculated from the summary file data.";
                    m_runreport.ReportChat(msg);
                }
            }
        }
    }
}

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

void ChainManager::DoOverallProfiles()
{
    Analyzer & analyzer = registry.GetAnalyzer();

    if(m_nregions>1)
    {
        ForceParameters overall_fp = m_chainpack.GetOverall().GetEstimates();
        DoubleVec1d overall_MLEs = overall_fp.GetGlobalParameters();
        double overall_like = m_chainpack.GetOverall().GetLlikemle();
        const RegionGammaInfo *pRegionGammaInfo = registry.GetRegionGammaInfo();
        if (pRegionGammaInfo)
            overall_MLEs.push_back(pRegionGammaInfo->GetMLE());
        analyzer.CalcProfiles(overall_MLEs, overall_like, FLAGLONG);
    }

    if (m_chainparam.RunProfileReps())
    {
        // JDEBUG--not finished yet--additional replicate runs at profile values
        // setup and run a set of new "replicate" chains in each region
        long int nparams(registry.GetForceSummary().GetAllNParameters());
        long int reg;
        for(reg = 0; reg < m_nregions; ++reg)
        {
            long int currrep = m_nreplicates;
            long int param;
            for(param = 0; param < nparams; ++param)
            {
                // pull the 97.5% param and start
                // resimulate starting tree???
                m_collectionmanager.StartChain(reg, currrep++, true);
                // do upper and lower 95% (97.5)
                // pull the 2.5% param and start
                m_collectionmanager.StartChain(reg, currrep++, true);
            }
        }
        // rerun the appropiate analysis
    }
} // ChainManager::DoOverallProfiles

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

void ChainManager::CloseSumOut()
{
    m_sumfilehandler.CloseSumOut();
    registry.GetUserParameters().SetWriteSumFile(false);
    m_writesumfile = false;
} // CloseSumOut

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

void ChainManager::SaveGeyerWeights(DoubleVec1d logGeyerWeights, long int region)
{
    if (m_logGeyerWeights.size() <= static_cast<unsigned long int>(region))
    {
        assert(m_logGeyerWeights.size() == static_cast<unsigned long int>(region));
        m_logGeyerWeights.push_back(logGeyerWeights);
    }
    else
    {
        m_logGeyerWeights[region] = logGeyerWeights;
    }
}

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

void ChainManager::OptimizeDataModels(long int reg)
{
    Region& region = registry.GetDataPack().GetRegion(reg);
    for (long int loc = 0; loc<region.GetNloci(); loc++)
    {
        Locus& locus = region.GetLocus(loc);
        unsigned long int cold = FindColdChain(m_temps);
        Tree* tree = m_temps[cold].GetTree();
        if (locus.GetDataModel()->OptimizeDataModel(tree, locus))
        {
            for (unsigned long int temp = 0; temp<m_temps.size(); temp++)
            {
                m_temps[temp].RecalculateDataLikes();
            }
        }
    }
}

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

void ChainManager::ResetAllAlphas()
{
    for (long int reg = 0; reg<registry.GetDataPack().GetNRegions(); reg++)
    {
        Region& region = registry.GetDataPack().GetRegion(reg);
        for (long int loc = 0; loc<region.GetNloci(); loc++)
        {
            region.GetLocus(loc).GetDataModel()->ResetAlpha();
        }
    }
}

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

const ChainPack &
ChainManager::GetChainPack() const
{
    return m_chainpack;
}

//____________________________________________________________________________________
