// $Id: lamarc.cpp,v 1.165 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 <iostream>
#include <string>
#include <vector>

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
#include <setjmp.h>
#include <signal.h>
#endif

#include "local_build.h"
#include "dynatracer.h"

#include "analyzer.h"
#include "arranger.h"
#include "bayesanalyzer_1d.h"
#include "chainmanager.h"
#include "chainout.h"
#include "chainparam.h"
#include "constants.h"
#include "curvefiles.h"
#include "datafilenamedialog.h"
#include "datapack.h"
#include "datatype.h"
#include "dialog.h"
#include "display.h"
#include "dlmodel.h"
#ifdef LAMARC_QA_SINGLE_DENOVOS
#include "force.h"
#endif //  LAMARC_QA_SINGLE_DENOVOS
#include "forcesummary.h"
#include "front_end_warnings.h"
#include "lamarc.h"
#include "lamarcheaderdialog.h"
#include "lamarcmenu.h"
#include "likelihood.h"
#include "maximizer.h"
#include "newmenuitems.h"
#include "nomenufilereaddialog.h"
#include "outputfile.h"
#include "parameter.h"
#include "parsetreeschema.h"
#include "parsetreetodata.h"
#include "parsetreetosettings.h"
#include "plotstat.h"
#include "region.h"
#include "registry.h"
#include "runreport.h"
#include "spreadsheet.h"
#include "stringx.h"
#include "treesum.h"
#include "ui_interface.h"
#include "ui_vars.h"
#include "userparam.h"
#include "xml.h"
#include "xml_report.h"
#include "xml_strings.h"

using namespace std;

//------------------------------------------------------------------------------------
// These "globals" (actually class statics) are relevant ONLY when DYNAMETER_LEVEL is defined,
// but when so, they must be defined before any other globals.

#ifdef DYNAMETER_LEVEL

// Pointer to current "toplevel" LocalDynameter object.
LocalDynameter * LocalDynameter::s_currentMeterPtr(NULL);

// Set at first call and used as zero-point of elapsed time for run.
unsigned long long int LocalDynameter::s_startClock(rdtsc());

// Set at first call and updated at end of each call, irrespective of trace level at call.
unsigned long long int LocalDynameter::s_lastClock(s_startClock);

// Accumulates maximum "Clocks since last trace before current call" time, over all calls to all functions.
unsigned long long int LocalDynameter::s_maxSinceLastBeforeCall(0ULL);

// Total number of function calls traced so far (all functions).
unsigned long long int LocalDynameter::s_totalNumberOfCalls(0ULL);

// Trace output file - opened in by LocalDynameter toplevel constructor and closed by toplevel destructor.
ofstream LocalDynameter::s_traceOut;

// Array of pointers to GlobalDynameter objects holding data globally (updated by dynamic LocalDynameter objects).
// All slots are explicitly set to NULL by toplevel call to StartDynameter().
GlobalDynameter * LocalDynameter::s_GlobalDynameterArray[DYNAMETER_ARRAYSIZE];

// Number of GlobalDynameter objects populating above array - ie, number of functions traced so far this run.
// This is used as an ordinal count for GlobalDynameter objects (gives order of each function's first call).
unsigned int LocalDynameter::s_GlobalDynameterCount(0u);

#if (DYNAMETER_LEVEL >= 3u)

// User-settable limit to depth of tracing based on number of calls to same function.
unsigned int LocalDynameter::s_traceCountLimit(0u);

// Flag/counter to control whether (and how) ellipsis indication is printed when dynamic tracing is suppressed.
unsigned int LocalDynameter::s_ellipsisCounter(0u);

// Count of total number of elided tracing printouts.
unsigned int LocalDynameter::s_printoutsElided(0u);

#endif // (DYNAMETER_LEVEL >= 3u)

// If using Dynameter, the first call must come before any other global data objects are allocated.
StartDynameter();

#endif // ifdef DYNAMETER_LEVEL

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

class TiXmlElement;

//------------------------------------------------------------------------------------
// The Registry singleton is global because everyone in the world needs to use it.

Registry registry;

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
extern jmp_buf prewrite;
// We attempt to deal with cases of a too-big output file, but
// we don't know how to make this work on Windows yet!
#endif

//------------------------------------------------------------------------------------
// This routine controls the menu, if any, and other startup issues.

void ParseOptions(long int argc, char** argv, bool* isBatch, bool* explicitExit, bool* hasFile, string & fileName)
{
    *isBatch=false;
    *explicitExit=false;
    *hasFile=false;
    for (int arg = 1; arg < argc; arg++)
    {
        string option(argv[arg]);
        if (option == "-b" || option == "--batch")
        {
            *isBatch = true;
        }
        else if (option == "-x" || option == "--explicit-exit")
        {
            *explicitExit = true;
        }
        else if (option == "-d" || option == "--denovo-sim-count")
        {
#ifdef LAMARC_QA_SINGLE_DENOVOS
            arg++;
            string numSimString = argv[arg];
            long numSim = static_cast<long>(atoi(numSimString.c_str()));
            registry.SetDenovoCount(numSim);
#else
            cerr << "Command line option \"-d\" only applies to LAMARC_QA_SINGLE_DENOVOS test. ";
            cerr << "Exiting." << endl;
            exit(lamarccodes::optionError);
#endif // LAMARC_QA_SINGLE_DENOVOS
        }
        else if (argv[arg][0] == '-')
        {
            // this is an error -- no such option
            cerr << "Unknown command line option \"" + option + "\"" << endl;
            cerr << "Exiting." << endl;
            exit(lamarccodes::optionError);
        }
        else if (*hasFile)
        {
            // this is an error -- don't know how to read two files
            cerr << "Cannot process argument \"" + option + "\"" << endl;
            cerr << "Already read \"" + fileName + "\" as file argument" << endl;
            cerr << "Exiting." << endl;
            exit(lamarccodes::optionError);
        }
        else
        {
            *hasFile = true;
            fileName = option;
        }
    }
}

//------------------------------------------------------------------------------------
// This routine controls the menu, if any, and other startup issues.

bool DoUserInput(bool batchmode, bool infileprovided, string infilename)
{
    // DEFAULTS and SETUP
    // DataPack & datapack = registry.GetDataPack();
    BranchTag::BeginBranchIDs(defaults::numSemiUniqueBranches);

    // Menu Subsystem

    ScrollingDisplay display;
    LamarcHeaderDialog header;
    header.InvokeMe(display);

    LamarcSchema        schema;
    FrontEndWarnings    warnings;
    XmlParser           parser(schema,warnings);

    if (!infileprovided)
    {
        if (batchmode)
        {
            // advises user that inputFileName will be read
            NoMenuFileReadDialog noMenuFileReadDialog(parser);
            noMenuFileReadDialog.InvokeMe(display);
        }
        else
        {
            // allows user to change inputFileName
            DataFileNameDialog dataDialog(parser);
            dataDialog.InvokeMe(display);
        }
    }
    else
    {
        parser.ParseFileData(infilename);
    }

    // print out any warnings that occured during parsing
    display.Warn(warnings.GetAndClearWarnings());

    // read the datapack portion of the input file
    ParseTreeToData dataParser(parser,registry.GetDataPack());
    dataParser.ProcessFileData();

    // read the settings portion of the input file
    UIInterface uiInterface(warnings,registry.GetDataPack(),parser.GetFileName());
    uiInterface.SetUndoRedoMode(undoRedoMode_FILE);
    ParseTreeToSettings settingsParser(parser,uiInterface);
    bool runProgram = true;

    // If we're in batch mode, default to no output (verbosity="none").  However,
    //  we allow this to be overwritten by the input file.
    if (batchmode)
    {
        uiInterface.GetCurrentVars().userparams.SetProgress(NONE);
        if (registry.GetARGfound())
        {
            if (!dataParser.CheckARGtree(uiInterface.doGetUIIdVec1d(uistr::validForces), batchmode))
            {
                runProgram = false;
            }
        }
    }

    settingsParser.ProcessFileSettings();

#ifndef LAMARC_QA_SINGLE_DENOVOS
    if (!batchmode)
    {
        // main menu: all menus and displays, setting of parameters
        uiInterface.SetUndoRedoMode(undoRedoMode_USER);
        LamarcMainMenu mainmenu(uiInterface);
        bool stayInLoop = true;
        while (stayInLoop)
        {
            menu_return_type menuCmd = mainmenu.InvokeMe(display);
            switch(menuCmd)
            {
                case menu_RUN:
                    uiInterface.AddWarning("(Already at top-level menu.  Type '.' to run LAMARC.)");
                    stayInLoop = false;
                    // if there were any ARG trees in the imput file, make sure they are still valid
                    if (registry.GetARGfound())
                    {
                        // make sure ARG trees are still valid
                        if (!dataParser.CheckARGtree(uiInterface.doGetUIIdVec1d(uistr::validForces), batchmode))
                        {
                            stayInLoop = true;
                        }
                    }
                    break;
                case menu_QUIT:
                    stayInLoop = false;
                    runProgram = false;
                    break;
                default:
                    break;
            }
        }
    }
#endif // LAMARC_QA_SINGLE_DENOVOS

    FinishRegistry(uiInterface);

    // read in the ARG trees and link them up
    if (registry.GetARGfound())
    {
        if (!dataParser.DoARGtree())
        {
            runProgram = false;
        }
    }

    return runProgram;

} // DoUserInput

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

//***********************************************************
// This routine constructs and registers objects which
// require user-generated information to create:
//    the proto-tree
//    the runtime reporter
//    the maximizer and its associated likelihoods
//************************************************************

void FinishRegistry(UIInterface & uiInterface)
{
#ifdef LAMARC_QA_SINGLE_DENOVOS

    ////////////////////////////////////////////////////
    // Check that we've only got one region and one replicate.
    long numReg = uiInterface.GetCurrentVars().datapackplus.GetNumRegions();
    long numRep = uiInterface.doGetLong(uistr::replicates);
    if(numReg != 1)
    {
        cerr << "Cannot do a denovo QA test run with other than one region. Exiting";
        exit(lamarccodes::unknownError);
    }
    if(numRep != 1)
    {
        cerr << "Cannot do a denovo QA test run with other than one replicate. Exiting";
        exit(lamarccodes::unknownError);
    }

    // set user parameters the way we know they need to be
    // for doing the denovo generation test

    // turn off bayesian -- we want likelihood so we can
    // get a single-tree profile out
    uiInterface.doSet(uistr::bayesian,"false",NO_ID());

    // this probably isn't necessary, as we do one rearrangement
    // but always reject
    uiInterface.doSet(uistr::dropArranger,"1",NO_ID());
    uiInterface.doSet(uistr::hapArranger,"0",NO_ID());
    uiInterface.doSet(uistr::sizeArranger,"0",NO_ID());

    // turn off heating
    uiInterface.doSet(uistr::heatedChainCount,"1",NO_ID());

    // adjust chain parameters
    uiInterface.doSet(uistr::initialChains,"0",NO_ID());
    string countStr = ToString(registry.GetDenovoCount());
    uiInterface.doSet(uistr::finalChains,countStr,NO_ID());
    uiInterface.doSet(uistr::finalInterval,"1",NO_ID());
    uiInterface.doSet(uistr::finalDiscard,"0",NO_ID());
    uiInterface.doSet(uistr::finalSamples,"1",NO_ID());

    // turn off profiling
    uiInterface.doSet(uistr::allProfilesOff,"",NO_ID());

    // turn off verbose output
    uiInterface.doSet(uistr::progress,"none",NO_ID());
    uiInterface.doSet(uistr::verbosity,"none",NO_ID());

#endif // LAMARC_QA_SINGLE_DENOVOS

    // build structures from uiInterface data -- ORDER MATTERS HERE
    registry.InstallUserParameters(uiInterface);
    registry.InstallForcesAllOverThePlace(uiInterface);
    registry.InstallChainParameters(uiInterface); //needs a ForceSummary, above
    registry.InstallDataModels(uiInterface);
    registry.FinalizeDataPack(uiInterface); //Needs installed datamodels

    ForceSummary & forcesummary = registry.GetForceSummary();

    Tree * prototree = forcesummary.CreateProtoTree();
    registry.Register(prototree);

    // create and register the runtime reporter
    // MUST DO THIS BEFORE MAKING THE ANALYZER
    verbosity_type progress = registry.GetUserParameters().GetProgress();

    RunReport* prunreport = new RunReport(forcesummary, progress);

    registry.Register(prunreport);

    // create and register the posterior likelihood objects

    // retrieve user-defined values from collection objects
    // obsolete? StringVec1d forcestr = forcesummary.GetForceString();
    long int nregs = registry.GetDataPack().GetNRegions();
    long int nreps = registry.GetChainParameters().GetNReps();
    long int paramsize = forcesummary.GetAllNParameters();

    // the maximizer itself
    Maximizer* pmax = new Maximizer(paramsize);
    pmax->SetConstraints(forcesummary.GetIdenticalGroupedParams());
    //The above could probably be moved into the maximizer constructor.
    registry.Register(pmax);

    const ParamVector params(true);
    if (!params.NumVariableParameters() &&
        registry.GetChainParameters().IsBayesian() &&
        !registry.GetDataPack().AnyMapping())
    {
        string msg = "All parameters are fixed in value, which in a Bayesian run "
            "means no estimation is possible.  This only makes sense if you are "
            "mapping a trait.  Please check your xml input file.";
        prunreport->ReportUrgent(msg);
        exit(0);
    }

    // single likelihood
    SinglePostLike* plsingle = new SinglePostLike(forcesummary, nregs, nreps, paramsize);
    registry.Register(plsingle);
    pmax->GradientGuideSetup(params, plsingle);

    // replicate likelihood
    ReplicatePostLike* plrep = new ReplicatePostLike(forcesummary, nregs, nreps, paramsize);
    pmax->GradientGuideSetup(params, plrep);
    registry.Register(plrep);

    // region likelihood
    // create the vector to handle parameter rescaling
    DoubleVec2d paramscalars(registry.GetDataPack().CreateParamScalarVector());
    RegionPostLike* plreg = new RegionPostLike(forcesummary, nregs, nreps, paramsize, paramscalars);
    pmax->GradientGuideSetup(params, plreg);
    registry.Register(plreg);

    // multi-region likelihood
    // with background mu rates varying over regions via a gamma distribution
    GammaRegionPostLike* plgammareg = new GammaRegionPostLike(forcesummary, nregs, nreps, paramsize, paramscalars);
    pmax->GradientGuideSetup(params, plgammareg);
    registry.Register(plgammareg);

    // Setup Analyzer subsystem {will do plots and profiles}
    Analyzer *analyzer = new Analyzer(forcesummary, params, pmax);
    registry.Register(analyzer);

    // But we might be doing a Bayesian analysis, in which case we need a
    // bayesian analyzer instead.  For now, just do a 1D analysis.

    BayesAnalyzer_1D *bayesanalyzer = new BayesAnalyzer_1D();
    registry.Register(bayesanalyzer);
} // FinishRegistry

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

int main(int argc, char **argv)
{
    // We attempt to deal with cases of a too-big output file, but
    // we don't know how to make this work on Windows yet!
#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)
    if (! setjmp(prewrite))
    {
        signal (SIGXFSZ, & CatchFileTooBigSignal);
    }
    else
    {
        cerr << "Something went unexpectedly wrong with file writing. Exiting.";
        exit(lamarccodes::unknownError);
    }
#endif

    int return_code = lamarccodes::cleanReturn;
    bool isbatch;
    bool explicitExit;

#ifdef STATIONARIES
    // clear out files used for stationaries record-keeping
    ofstream of;
    of.open(INTERVALFILE.c_str(),ios::trunc);
    of.close();
    of.open(MIGFILE.c_str(),ios::trunc);
    of.close();
    of.open(DISFILE.c_str(),ios::trunc);
    of.close();
    of.open(RECFILE.c_str(),ios::trunc);
    of.close();
#endif // STATIONARIES

#ifdef LAMARC_QA_SINGLE_DENOVOS
    // empty out file we'll write data to
    ofstream of_denovo;
    ofstream of_denovo_info;

    of_denovo.open(SINGLE_DENOVO_FILE.c_str(),ios::trunc);
    of_denovo.close();
    of_denovo_info.open(SINGLE_DENOVO_INFO.c_str(),ios::trunc);
    of_denovo_info.close();
#endif // LAMARC_QA_SINGLE_DENOVOS

    try
    {
        // handle user input including menu and data file
        bool hasfile;
        string filename;
        ParseOptions(argc, argv, & isbatch, &explicitExit, & hasfile, filename);

        bool runProgram = DoUserInput(isbatch,hasfile,filename);

#ifdef LAMARC_QA_SINGLE_DENOVOS
        // create header for denovo_params.txt
        // we do it after user input because we need to know
        // which params are valid
        //
        // also show only the simulation parameters for the info file
        of_denovo.open(SINGLE_DENOVO_FILE.c_str(),ios::out | ios::app);
        of_denovo_info.open(SINGLE_DENOVO_INFO.c_str(),ios::trunc);

        const ParamVector pvec(true);
        const ForceParameters & fp = registry.GetForceSummary().GetStartParameters();
        DoubleVec1d sv = fp.GetGlobalParameters();

        StringVec1d names;
        DoubleVec1d startValues;

        ParamVector::const_iterator pi;
        size_t startI;

        for(pi = pvec.begin(), startI=0; pi != pvec.end(), startI < sv.size(); pi++,startI++)
        {
            if(pi->IsValidParameter())
            {
                names.push_back(pi->GetShortName());
                startValues.push_back(sv[startI]);
            }
        }

        assert(names.size() == startValues.size());

        for(startI=0; startI < names.size(); startI++)
        {
            if(startI != 0)
            {
                of_denovo << "\t";
                of_denovo_info << "\t";
            }
            of_denovo << names[startI];
            of_denovo_info << names[startI];
        }
        // carriage return for info file only, still more in denovo_params.txt
        of_denovo_info << endl;

        ////////////////////////////////////////////////////////////////
        // these are for the denovo_params file only
        //

        if(registry.GetForceSummary().CheckForce(force_MIG) ||
           registry.GetForceSummary().CheckForce(force_DIVMIG))
        {
            deque<bool> migsToPrint;
            for(pi = pvec.begin(), startI=0; pi != pvec.end(), startI < sv.size(); pi++,startI++)
            {
                if(pi->IsForce(force_MIG) || pi->IsForce(force_DIVMIG)){
                    if(pi->IsValidParameter())
                    {
                        of_denovo << "\t" << pi->GetShortName() << "Count";
                        migsToPrint.push_back(true);
                    }
                    else
                    {
                        migsToPrint.push_back(false);
                    }
                }
            }
            registry.SetMigsToPrint(migsToPrint);
        }

        if(registry.GetForceSummary().CheckForce(force_REC))
        {
            of_denovo << "\t" << "RecCount";
        }

        of_denovo << endl;
        of_denovo.close();

        ////////////////////////////////////////////////////////////////
        // these are for the denovo_info file only
        //
        for(startI=0; startI < startValues.size(); startI++)
        {
            if(startI != 0)
            {
                of_denovo_info << "\t";
            }
            of_denovo_info << startValues[startI];
        }
        of_denovo_info << endl;
        of_denovo_info << "==============";
        of_denovo_info << endl;
        of_denovo_info.close();
#endif // LAMARC_QA_SINGLE_DENOVOS

#ifdef JSIM  // call with true to get separate files by region and population
        registry.GetDataPack().WritePopulationXMLFiles(false);
        string ofname("flucinfile");
        registry.GetDataPack().WriteFlucFile(ofname);
#endif // JSIM

        // Run the chainmanager.
        Maximizer & maximizer = registry.GetMaximizer();

        // Prepare for the results.
        RunReport & runreport = registry.GetRunReport();

        if (runProgram)
        {
            try
            {
                XMLOutfile xmlout;
                xmlout.Display();
            }
            catch (file_error & e)
            {
                throw e;
            }

#ifdef DYNAMETER_LEVEL
            // This controls whether user-interaction startup time is to be excluded from the global measures or not.
            // If tracing/metering, restart the "epoch" timers when user interaction is done and "real" processing starts.
            LocalDynameter::s_lastClock = LocalDynameter::s_startClock = rdtsc();
#endif // DYNAMETER_LEVEL

            // Run the main program.
            ChainManager chainmanager(runreport, maximizer);
            chainmanager.DoAllChains();

            // EWFIX -- does this belong in chainmanager.DoAllChains() ??

#ifdef LAMARC_QA_SINGLE_DENOVOS
            // next to no reporting for LAMARC_QA_SINGLE_DENOVOS
            ofstream of;
            of.open(SINGLE_DENOVO_INFO.c_str(), ios::app);


            of << setiosflags ( ios_base::right ) << setw ( 10 );
            of << registry.GetDenovoTreeRejectCount() << " denovo trees failed during construction" << endl;
            of << setiosflags ( ios_base::right ) << setw ( 10 );
            of << registry.GetDenovoCount() << " denovo trees successfully constructed" << endl;
            of << setiosflags ( ios_base::right ) << setw ( 10 );
            of << registry.GetDenovoMaxRejectCount() << " constructed trees failed in maximizer " << endl;
            of.close();
#else
#ifndef STATIONARIES
            string xmlReportFileName = registry.GetUserParameters().GetXMLReportFileName();
            assert (!xmlReportFileName.empty());
            XMLReport xmlReportFile(xmlReportFileName);
            TiXmlElement * topElem = xmlReportFile.Write(chainmanager);
            string profilePrefix = registry.GetUserParameters().GetProfilePrefix();
            WriteProfileSpreads(profilePrefix,topElem);
            bool writeCurves = registry.GetUserParameters().GetWriteCurveFiles();
            if(writeCurves && registry.GetChainParameters().IsBayesian())
            {
                string curvefilePrefix = registry.GetUserParameters().GetCurveFilePrefix();
                BayesAnalyzer_1D & bayesAnalyzer = registry.GetBayesAnalyzer_1D();
                WriteConsolidatedCurveFiles(curvefilePrefix,bayesAnalyzer);
            }
#endif // STATIONARIES

            if (registry.GetUserParameters().GetProgress() != NONE)
            {
                string msg = "Output written to ";
                msg += registry.GetUserParameters().GetResultsFileName();
                runreport.ReportUrgent(msg);
                if (registry.GetUserParameters().GetWriteSumFile())
                {
                    msg = "Output summary file written to ";
                    msg += registry.GetUserParameters().GetTreeSumOutFileName();
                    runreport.ReportUrgent(msg);
                }

                StringVec1d profilenames = registry.GetUserParameters().GetProfileNames();
                if (profilenames.size() > 0)
                {
                    msg = "Wrote to profile file(s):  " + profilenames[0];
                    for (unsigned long i=1; i<profilenames.size(); i++)
                    {
                        msg += ", " + profilenames[i];
                    }
                    runreport.ReportUrgent(msg);
                }

                StringVec1d curvefilenames = registry.GetUserParameters().GetCurveFileNames();
                if (curvefilenames.size() > 0)
                {
                    //LS NOTE: not GetWriteCurveFiles() because if not bayesian, that
                    // parameter is meaningless.  This version is much safer.
                    msg = "Wrote to curve file(s):  " + curvefilenames[0];
                    for (unsigned long int i = 1; i < curvefilenames.size(); i++)
                    {
                        msg += ", " + curvefilenames[i];
                    }
                    runreport.ReportUrgent(msg);
                }

                StringVec1d mapfilenames = registry.GetUserParameters().GetMapFileNames();
                if (mapfilenames.size() > 0)
                {
                    msg = "Wrote to mapping file(s):  " + mapfilenames[0];
                    for (unsigned long int i = 1; i < mapfilenames.size(); i++)
                    {
                        msg += ", " + mapfilenames[i];
                    }
                    runreport.ReportUrgent(msg);
                }

                set<string> tracefilenames = registry.GetUserParameters().GetTraceFileNames();
                if (tracefilenames.size() > 0)
                {
                    msg = "Wrote to Tracer file(s):";
                    set<string>::iterator tname = tracefilenames.begin();
                    msg += " " + *tname;
                    for (tname++; tname != tracefilenames.end(); tname++)
                    {
                        msg += ", " + *tname;;
                    }
                    runreport.ReportUrgent(msg);
                }

                runreport.ReportUrgent("\nProgram completed.", 0);
                runreport.ReportUrgent("You may now exit manually if we haven't already exited automatically.", 0);
                runreport.ReportUrgent("In either case, you may now examine your results.", 0);
                runreport.ReportUrgent("For assistance interpreting your results, see "
                                       "http://evolution.gs.washington.edu/lamarc/documentation/"
                                       " (or your local copy of the documentation).",
                                       0);
            }
#endif // !LAMARC_QA_SINGLE_DENOVOS
        }
    }

    catch (bad_alloc)
    {
        cerr << endl << endl
             << "LAMARC has terminated because an insufficient amount of memory"
             << endl
             << "is available.  If you are running additional programs"
             << endl
             << "simultaneously on your computer, try terminating them and"
             << endl
             << "re-executing LAMARC.  Or, try running LAMARC on a different"
             << endl << "computer that has more memory available."
             << endl << endl;
        return_code = return_code | lamarccodes::badAllocation;
    }

    catch(const unrecognized_tag_error & e)
    {
        cerr <<  xmlstr::XML_IERR_NO_TAG_0
            + e.what()
            + xmlstr::XML_IERR_NO_TAG_1
            + ToString(e.where())
            + xmlstr::XML_IERR_NO_TAG_2
             << endl ;
        return_code = return_code | lamarccodes::fileError;
    }

    catch (exception & ex)
    {
        cerr << ex.what() << endl;
        return_code = return_code | lamarccodes::unknownError;
    }

    // needed for windows clickable executables
    // Mac handles this with an "-x" in the invoking apple script
#if defined(LAMARC_COMPILE_MSWINDOWS)
    GetCloseConfirmation();
#else
    if(explicitExit)
    {
        GetCloseConfirmation();
    }
#endif

    return return_code;

} // LAMARC main routine

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

void GetCloseConfirmation()
{
    cout << "Press Enter to Quit" << endl;
    string dummyString;
    MyCinGetline(dummyString);
}

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

#if defined(LAMARC_COMPILE_LINUX) || defined(LAMARC_COMPILE_MACOSX)

void CatchFileTooBigSignal(int)
{
    //The kernel has sent a signal that the file size limit has been exceeded.
    //We're going to assume that this was for the summary file writing, since
    // that's the biggest
    // Stop writing to the summary file.
    registry.GetRunReport().ReportUrgent("File size exceeded for summary file writing--continuing,"
                                         " but without writing to the summary file any more.  "
                                         "Try the command 'unlimit filesize' if on a UNIX system.");
    longjmp(prewrite, 1);
    //We set this jumppoint in ChainManager at a point where we can close
    // the summary file and stop writing to it.
}

#endif

//____________________________________________________________________________________
