// $Id: parsetreetosettings.cpp,v 1.86 2018/01/03 21:33:06 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 "local_build.h"
#include "constants.h"
#include "errhandling.h"
#include "mathx.h"
#include "parsetreetosettings.h"
#include "parsetreewalker.h"
#include "stringx.h"
#include "ui_constants.h"               // for uiconst::GLOBAL_ID
#include "ui_interface.h"
#include "ui_regid.h"
#include "ui_strings.h"
#include "ui_vars.h"
#include "xml.h"
#include "xml_strings.h"

using std::string;

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

ParseTreeToSettings::ParseTreeToSettings(XmlParser& parser, UIInterface& ui)
    :
    ParseTreeWalker(parser),
    uiInterface(ui)
{
}

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

ParseTreeToSettings::~ParseTreeToSettings()
{
}

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

void
ParseTreeToSettings::ProcessFileSettings()
{
    TiXmlElement * docElement = m_parser.GetRootElement();

    const char * tagValue = docElement->Value();
    string tagString(tagValue);
    bool matches = CaselessStrCmp(xmlstr::XML_TAG_LAMARC,tagValue);
    if(!matches)
    {
        string msg = m_parser.GetFileName() + ": " + xmlstr::XML_ERR_NOT_LAMARC;
        m_parser.ThrowDataError(msg);
    }

    vector<TiXmlElement *> globalModelElements
        = getAllOptionalDescendantElements(docElement,xmlstr::XML_TAG_MODEL);
    for (unsigned long i=0; i<globalModelElements.size(); ++i)
    {
        DoDLModel(globalModelElements[i],uiconst::GLOBAL_ID);
    }

    DoDataModels(singleRequiredChild(docElement,xmlstr::XML_TAG_DATA));
    DoTraits(singleRequiredChild(docElement,xmlstr::XML_TAG_DATA));

    TiXmlElement * chainsElement
        = singleOptionalChild(docElement,xmlstr::XML_TAG_CHAINS);
    if(chainsElement != NULL)
    {
        DoChainParams(chainsElement);
    }

    TiXmlElement * formatElement
        = singleOptionalChild(docElement,xmlstr::XML_TAG_FORMAT);
    if(formatElement != NULL)
    {
        DoUserParams(formatElement);
    }

    // This *must* be after DoChainParams because DIVMIG requires
    // Bayesian and that the Epoch Arranger be on.
    TiXmlElement * forcesElement
        = singleOptionalChild(docElement,xmlstr::XML_TAG_FORCES);
    if(forcesElement != NULL)
    {
        DoForces(forcesElement);
    }
}

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

void
ParseTreeToSettings::DoOptionalElement(TiXmlElement* ancestor, string tagName,string uiLabel, UIId id)
{
    TiXmlElement * node = singleOptionalChild(ancestor,tagName);
    if(node == NULL)
    {
        return;
    }
    try
    {
        uiInterface.doSet(uiLabel,getNodeText(node),id);
    }
    catch (data_error & d)
    {
        m_parser.ThrowDataError(d.what(), node->Row());
    }
}

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

void
ParseTreeToSettings::DoRequiredElement(TiXmlElement* ancestor, string tagName,string uiLabel, UIId id)
{
    TiXmlElement * node = singleRequiredChild(ancestor,tagName);
    if(node == NULL)
    {
        m_parser.ThrowDataError(xmlstr::XML_ERR_MISSING_TAG_HIER_0
                                + tagName
                                + xmlstr::XML_ERR_MISSING_TAG_HIER_1
                                + ancestor->Value()
                                + xmlstr::XML_ERR_MISSING_TAG_HIER_2,
                                node->Row());
    }
    uiInterface.doSet(uiLabel,getNodeText(node),id);
}

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

void
ParseTreeToSettings::DoDataModels(TiXmlElement * dataElement)
{
    TiXmlNode * child = NULL;

    long regionNumber = 0;

    while((child = dataElement->IterateChildren(xmlstr::XML_TAG_REGION,child)))
    {
        TiXmlElement * regionElement = child->ToElement();
        DoRegionDataModels(regionElement,regionNumber);
        regionNumber++;
    }
}

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

void
ParseTreeToSettings::DoRegionDataModels(TiXmlElement * regionElement, long regionId)
{
    TiXmlNode * child = NULL;

    long maxLoci = uiInterface.GetCurrentVars().datapackplus.GetMaxLoci();
    long locusId = 0;

    while((child = regionElement->IterateChildren(xmlstr::XML_TAG_MODEL,child)))
    {
        TiXmlElement * modelElement = child->ToElement();
        // EWFIX.P4 LOCUS.HACK.HACK.HACK
        // probably making more than we need
        // also need to handle per-locus models later
        // probably does something awful for mismatched types
        //
        //JDEBUG--evil hack
        // we now handle multiple loci, but...ick...sequential implied
        // ordering
        if (locusId >= maxLoci)  // too many loci?
        {
            std::string errstring("Warning:  More datamodels found");
            errstring += " than segments, only the first ";
            errstring += ToString(locusId);
            errstring += " datamodels were used.\n";
            uiInterface.AddWarning(errstring);
            return;
        }

        DoDLModel(modelElement,regionId,locusId);
        ++locusId;
    }
    // JDEBUG
    // ideally we would now fill out the remaining loci (if any) with
    // default datamodels for their datatype...but this may conflict
    // with registry::InstallDataModel/registry::CreateDataModel and
    // it's use of the single "regional" datamodel
}

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

void
ParseTreeToSettings::DoDLModel(TiXmlElement * modelElement, long regionId, long locusId)
{
    string modelName = getNodeAttributeValue(modelElement,xmlstr::XML_ATTRTYPE_NAME);
    //Here is where we change the regionId to the correct flag if it had been
    // set to GLOBAL_ID.  It must be recursive for the KAllele case (for now).
    if (regionId == uiconst::GLOBAL_ID)
    {
        switch (ProduceModelTypeOrBarf(modelName))
        {
            case F84:
            case GTR:
                DoDLModel(modelElement, uiconst::GLOBAL_DATAMODEL_NUC_ID);
                return;
            case Brownian:
            case Stepwise:
            case MixedKS:
                DoDLModel(modelElement, uiconst::GLOBAL_DATAMODEL_MSAT_ID);
                return;
            case KAllele:
                //Here we have to set the global model for *both* the microsat *and*
                // 'kallele' data.
                DoDLModel(modelElement, uiconst::GLOBAL_DATAMODEL_MSAT_ID);
                DoDLModel(modelElement, uiconst::GLOBAL_DATAMODEL_KALLELE_ID);
                return;
        }
        assert(false); //Uncaught model type.
        regionId = uiconst::GLOBAL_DATAMODEL_NUC_ID;
    }
    assert(regionId != uiconst::GLOBAL_ID);

    UIId locusUIId(regionId,locusId);

    uiInterface.doSet(uistr::dataModel,modelName,locusUIId);

    // comments below show which models should allow the setting

    // base frequencies -- F84, GTR (but don't allow calculated)
    TiXmlElement * baseFrequenciesElement
        = singleOptionalChild(modelElement,xmlstr::XML_TAG_BASE_FREQS);
    if(baseFrequenciesElement != NULL)
    {
        DoBaseFrequencies(baseFrequenciesElement,locusUIId);
    }
    // ttratio -- F84
    DoOptionalElement(modelElement,xmlstr::XML_TAG_TTRATIO,uistr::TTRatio,locusUIId);
    // normalize -- F84 GTR Stepwise KAllele
    DoOptionalElement(modelElement,xmlstr::XML_TAG_NORMALIZE,uistr::normalization,locusUIId);
    // gtrrates -- GTR
    TiXmlElement * gtrElement
        = singleOptionalChild(modelElement,xmlstr::XML_TAG_GTRRATES);
    if(gtrElement != NULL)
    {
        DoGTR(gtrElement,locusUIId);
    }
    // error rate -- GTR and F84
    DoOptionalElement(modelElement,xmlstr::XML_TAG_PER_BASE_ERROR_RATE,uistr::perBaseErrorRate,locusUIId);
    // alpha -- mixedKS
    DoOptionalElement(modelElement,xmlstr::XML_TAG_ALPHA,uistr::alpha,locusUIId);
    // optimize -- mixedKS
    DoOptionalElement(modelElement,xmlstr::XML_TAG_ISOPT,uistr::optimizeAlpha,locusUIId);

    // categories F84 GTR Stepwise Brownian KAllele
    TiXmlElement * categoriesElement
        = singleOptionalChild(modelElement,xmlstr::XML_TAG_CATEGORIES);
    if(categoriesElement != NULL)
    {
        DoCategories(categoriesElement,locusUIId);
    }

    // relative mutation rate -- all
    DoOptionalElement(modelElement,xmlstr::XML_TAG_RELATIVE_MURATE,uistr::relativeMuRate,locusUIId);
} // DoDLModel

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

void
ParseTreeToSettings::DoCategories(TiXmlElement * categoriesElement, UIId locusUIId)
{
    DoOptionalElement(categoriesElement,xmlstr::XML_TAG_NUM_CATEGORIES,uistr::categoryCount,locusUIId);
    DoOptionalElement(categoriesElement,xmlstr::XML_TAG_AUTOCORRELATION,uistr::autoCorrelation,locusUIId);

    TiXmlElement * catRates = singleOptionalChild(categoriesElement,xmlstr::XML_TAG_RATES);
    TiXmlElement * catProbs = singleOptionalChild(categoriesElement,xmlstr::XML_TAG_PROBABILITIES);

    if(catRates != NULL)
    {
        StringVec1d strings = getNodeTextSplitOnWhitespace(catRates);
        long catCount = uiInterface.doGetLong(uistr::categoryCount,locusUIId);
        long numToSet = catCount;
        if((long)strings.size() > catCount)
        {
            uiInterface.AddWarning("Warning:  The number of supplied category rates and/or probabilities is greater than the number of categories.  Discarding the extras.");
        }
        else
        {
            if((long)strings.size() < catCount)
            {
                uiInterface.AddWarning("Warning:  The number of supplied category rates and/or probabilities is less than the number of categories.  Using defaults for the extras.");
                numToSet = strings.size();
            }
        }

        for(long index=0;index < numToSet; index++)
        {
            UIId innerId(locusUIId.GetIndex1(),locusUIId.GetIndex2(),index);
            uiInterface.doSet(uistr::categoryRate,strings[index],innerId);
        }
    }
    if(catProbs != NULL)
    {
        StringVec1d strings = getNodeTextSplitOnWhitespace(catProbs);
        long catCount = uiInterface.doGetLong(uistr::categoryCount,locusUIId);
        long numToSet = catCount;
        if((long)strings.size() > catCount)
        {
            uiInterface.AddWarning("Warning:  The number of supplied category rates and/or probabilities is greater than the number of categories.  Discarding the extras.");
        }
        else
        {
            if((long)strings.size() < catCount)
            {
                uiInterface.AddWarning("Warning:  The number of supplied category rates and/or probabilities is less than the number of categories.  Using defaults for the extras.");
                numToSet = strings.size();
            }
        }

        for(long index=0;index < numToSet; index++)
        {
            UIId innerId(locusUIId.GetIndex1(),locusUIId.GetIndex2(),index);
            uiInterface.doSet(uistr::categoryProbability,strings[index],innerId);
        }
    }
}

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

void
ParseTreeToSettings::DoBaseFrequencies(TiXmlElement * baseFreqsElem, UIId thisId)
{

    // kinda grotty, but we want to ignore case differences
    string frequencies = getNodeText(baseFreqsElem);
    string tag = xmlstr::XML_TAG_CALCULATED;
    LowerCase(frequencies);
    LowerCase(tag);
    string::size_type index = frequencies.find(tag);

    if(index != std::string::npos)
    {
        uiInterface.doSet(uistr::freqsFromData,"true",thisId);
    }
    else
    {
        StringVec1d strings;
        bool gotStrings = FromString(frequencies,strings);
        if(gotStrings)
        {
            if(strings.size() == 4)
            {
                uiInterface.doSet(uistr::baseFrequencyA,strings[0],thisId);
                uiInterface.doSet(uistr::baseFrequencyC,strings[1],thisId);
                uiInterface.doSet(uistr::baseFrequencyG,strings[2],thisId);
                uiInterface.doSet(uistr::baseFrequencyT,strings[3],thisId);
            }
            else if (strings.size() == 0)
            {
                uiInterface.AddWarning("Warning:  no supplied base frequencies; using a set of defaults.");

            }
            else
            {
                uiInterface.AddWarning("Warning:  wrong number of supplied base frequencies; using a set of defaults.");
            }
        }
        else
        {
            uiInterface.AddWarning("Warning:  no supplied base frequencies; using a set of defaults.");
        }
    }
} // DoBaseFrequencies

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

void
ParseTreeToSettings::DoGTR(TiXmlElement * gtrElem, UIId thisId)
{
    StringVec1d strings = getNodeTextSplitOnWhitespace(gtrElem);
    if(strings.size() == 6)
    {
        uiInterface.doSet(uistr::gtrRateAC,strings[0],thisId);
        uiInterface.doSet(uistr::gtrRateAG,strings[1],thisId);
        uiInterface.doSet(uistr::gtrRateAT,strings[2],thisId);
        uiInterface.doSet(uistr::gtrRateCG,strings[3],thisId);
        uiInterface.doSet(uistr::gtrRateCT,strings[4],thisId);
        uiInterface.doSet(uistr::gtrRateGT,strings[5],thisId);
    }
    else if (strings.size() == 0)
    {
        uiInterface.AddWarning("Warning:  no GTR rates supplied:  using a set of defaults.");
    }
    else
    {
        uiInterface.AddWarning("Warning:  incorrect number of GTR rates supplied:  using a set of defaults.");
    }
} // DoGTR

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

void
ParseTreeToSettings::DoChainParams(TiXmlElement * chainsElement)
{
    DoOptionalElement(chainsElement,xmlstr::XML_TAG_REPLICATES,uistr::replicates);

    TiXmlElement * initialChainElement
        = singleOptionalChild(chainsElement,xmlstr::XML_TAG_INITIAL);
    if(initialChainElement != NULL)
    {
        DoOptionalElement(initialChainElement,xmlstr::XML_TAG_NUMBER,uistr::initialChains);
        DoOptionalElement(initialChainElement,xmlstr::XML_TAG_SAMPLES,uistr::initialSamples);
        DoOptionalElement(initialChainElement,xmlstr::XML_TAG_INTERVAL,uistr::initialInterval);
        DoOptionalElement(initialChainElement,xmlstr::XML_TAG_DISCARD,uistr::initialDiscard);
    }

    TiXmlElement * finalChainElement
        = singleOptionalChild(chainsElement,xmlstr::XML_TAG_FINAL);
    if(finalChainElement != NULL)
    {
        DoOptionalElement(finalChainElement,xmlstr::XML_TAG_NUMBER,uistr::finalChains);
        DoOptionalElement(finalChainElement,xmlstr::XML_TAG_SAMPLES,uistr::finalSamples);
        DoOptionalElement(finalChainElement,xmlstr::XML_TAG_INTERVAL,uistr::finalInterval);
        DoOptionalElement(finalChainElement,xmlstr::XML_TAG_DISCARD,uistr::finalDiscard);
    }

    TiXmlElement * heatingElement
        = singleOptionalChild(chainsElement,xmlstr::XML_TAG_HEATING);
    if(heatingElement != NULL)
    {
        TiXmlElement * temperatures
            = singleOptionalChild(heatingElement,xmlstr::XML_TAG_TEMPERATURES);
        if(temperatures != NULL) DoTemperatures(getNodeTextSplitOnWhitespace(temperatures));

        TiXmlElement * intervals
            = singleOptionalChild(heatingElement,xmlstr::XML_TAG_SWAP_INTERVAL);
        if(intervals != NULL) DoSwapInterval(intervals);

        DoOptionalElement(heatingElement,xmlstr::XML_TAG_HEATING_STRATEGY,uistr::tempAdapt);
    }

    // must do processing for XML_TAG_BAYESIAN_ANALYSIS before processing for
    // the XML_TAG_BAYESIAN strategy element because if they are inconsistent
    // we need to throw--the BAYESIAN_ANALYSIS tag takes precedence.
    DoOptionalElement(chainsElement,xmlstr::XML_TAG_BAYESIAN_ANALYSIS,uistr::bayesian);
    TiXmlElement * strategyElement
        = singleOptionalChild(chainsElement,xmlstr::XML_TAG_STRATEGY);
    if(strategyElement != NULL)
    {
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_RESIMULATING,uistr::dropArranger);
        uiInterface.GetCurrentVars().chains.RescaleDefaultSizeArranger();
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_TREESIZE,uistr::sizeArranger);
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_STAIRARRANGER,uistr::stairArranger);
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_HAPLOTYPING,uistr::hapArranger);
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_BAYESIAN,uistr::bayesArranger);
        DoOptionalElement(strategyElement,xmlstr::XML_TAG_EPOCHSIZEARRANGER,uistr::epochSizeArranger);
    }

} // DoChainParams

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

void
ParseTreeToSettings::DoTemperatures(StringVec1d temperatures)
{
    if(temperatures.empty())
    {
        uiInterface.AddWarning("Warning:  No supplied temperatures for heated chains:  using a set of defaults.");
    }
    else
    {
        uiInterface.doSet(uistr::heatedChainCount,ToString(temperatures.size()));
        for(long index = 0; index < (long)temperatures.size(); index++)
        {
            uiInterface.doSet(uistr::heatedChain,temperatures[index],UIId(index));
        }
    }
}

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

void
ParseTreeToSettings::DoSwapInterval(TiXmlElement * intervals)
{
    StringVec1d values = getNodeTextSplitOnWhitespace(intervals);
    if(values.size() > 0)
    {
        uiInterface.doSet(uistr::tempInterval,values[values.size()-1]);
    }
    if(values.empty())
    {
        uiInterface.AddWarning("Warning:  empty <" + xmlstr::XML_TAG_SWAP_INTERVAL + "> tag found; using defaults.");
    }

} // DoSwapInterval

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

void
ParseTreeToSettings::DoSeedParams(TiXmlElement * formatElement)
{
    TiXmlElement * node1 = singleOptionalChild(formatElement,xmlstr::XML_TAG_SEED);
    TiXmlElement * node2 = singleOptionalChild(formatElement,xmlstr::XML_TAG_SEED_FROM_CLOCK);
    if(node1 != NULL && node2 != NULL)
    {
        m_parser.ThrowDataError(xmlstr::XML_ERR_BOTH_SEED_TYPES_0
                                + ToString(node1->Row())
                                + xmlstr::XML_ERR_BOTH_SEED_TYPES_1
                                + ToString(node2->Row())
                                + xmlstr::XML_ERR_BOTH_SEED_TYPES_2
                                + m_parser.GetFileName()
                                + xmlstr::XML_ERR_BOTH_SEED_TYPES_3
            );
    }

    DoOptionalElement(formatElement,xmlstr::XML_TAG_SEED,uistr::randomSeed);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_SEED_FROM_CLOCK,uistr::setOldSeedFromClock);
}

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

void
ParseTreeToSettings::DoUserParams(TiXmlElement * formatElement)
{
    DoOptionalElement(formatElement,xmlstr::XML_TAG_VERBOSITY,uistr::verbosity);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_PROGRESS_REPORTS,uistr::progress);

    TiXmlElement * plottingElement
        = singleOptionalChild(formatElement,xmlstr::XML_TAG_PLOTTING);
    if(plottingElement != NULL)
    {
        DoOptionalElement(plottingElement,xmlstr::XML_TAG_POSTERIOR,uistr::plotPost);
    }

    DoSeedParams(formatElement);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_RESULTS_FILE,uistr::resultsFileName);
    //LS NOTE:  These are off by default, so we only warn that there's a
    // problem if we turn them off, meaning we should set the filename first.
    DoOptionalElement(formatElement,xmlstr::XML_TAG_OLD_SUMMARY_FILE,uistr::treeSumInFileName);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_IN_SUMMARY_FILE,uistr::treeSumInFileName);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_OUT_SUMMARY_FILE,uistr::treeSumOutFileName);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_IN_SUMMARY,uistr::treeSumInFileEnabled);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_OUT_SUMMARY,uistr::treeSumOutFileEnabled);
#ifdef LAMARC_QA_TREE_DUMP
    DoOptionalElement(formatElement,xmlstr::XML_TAG_ARGFILE_PREFIX,uistr::argFilePrefix);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_ARGFILES,uistr::useArgFiles);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_MANY_ARGFILES,uistr::manyArgFiles);
#endif // LAMARC_QA_TREE_DUMP
    DoOptionalElement(formatElement,xmlstr::XML_TAG_NEWICKTREEFILE_PREFIX,uistr::newickTreeFilePrefix);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_NEWICKTREEFILE,uistr::useNewickTreeFiles);
    //LS NOTE:  And these two are on by default, so we warn only if it's still
    // on when we change the name.
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_CURVEFILES,uistr::useCurveFiles);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_CURVEFILE_PREFIX,uistr::curveFilePrefix);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_PROFILE_PREFIX,uistr::profileprefix);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_RECLOCFILE,uistr::useReclocFiles);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_RECLOCFILE_PREFIX,uistr::reclocFilePrefix);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_USE_TRACEFILE,uistr::useTraceFiles);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_TRACEFILE_PREFIX,uistr::traceFilePrefix);
    //LS NOTE:  And this one we always write, so there are no warnings about
    // the setting.
    DoOptionalElement(formatElement,xmlstr::XML_TAG_OUT_XML_FILE,uistr::xmlOutFileName);
    DoOptionalElement(formatElement,xmlstr::XML_TAG_REPORT_XML_FILE,uistr::xmlReportFileName);

} // DoUserParams

//------------------------------------------------------------------------------------
// Force parameter functions
//------------------------------------------------------------------------------------

void
ParseTreeToSettings::DoForces(TiXmlElement * forcesElement)
{
    DoForceIfPresent(forcesElement,force_DIVERGENCE,xmlstr::XML_TAG_DIVERGENCE);
    DoForceIfPresent(forcesElement,force_COAL,xmlstr::XML_TAG_COALESCENCE);
    DoForceIfPresent(forcesElement,force_MIG,xmlstr::XML_TAG_MIGRATION);
    DoForceIfPresent(forcesElement,force_REC,xmlstr::XML_TAG_RECOMBINATION);
    DoForceIfPresent(forcesElement,force_GROW,xmlstr::XML_TAG_GROWTH);
    DoForceIfPresent(forcesElement,force_LOGISTICSELECTION,
                     xmlstr::XML_TAG_LOGISTICSELECTION);
    DoForceIfPresent(forcesElement,force_LOGSELECTSTICK,
                     xmlstr::XML_TAG_STOCHASTICSELECTION);
    DoForceIfPresent(forcesElement,force_DISEASE,xmlstr::XML_TAG_DISEASE);
    DoForceIfPresent(forcesElement,force_REGION_GAMMA,
                     xmlstr::XML_TAG_REGION_GAMMA);
    DoForceIfPresent(forcesElement,force_DIVMIG,xmlstr::XML_TAG_DIVMIG);

} // DoForces

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

void
ParseTreeToSettings::DoForceIfPresent(
    TiXmlElement * forcesElement,
    force_type forcetype,
    const string& forcetag)
{
    TiXmlElement * forceElement = singleOptionalChild(forcesElement,forcetag);

    if(forceElement != NULL)
    {
        DoForce(forceElement,forcetype);
    }

}

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

void
ParseTreeToSettings::DoForce(TiXmlElement* forceElement, force_type forcetype)
{
    UIId forceId(forcetype);

    string forceOnOff = getNodeAttributeValue(forceElement,xmlstr::XML_ATTRTYPE_VALUE);
    if(!forceOnOff.empty())
    {
        uiInterface.doSet(uistr::forceOnOff,forceOnOff,forceId);
    }
    else
    {
        uiInterface.doSet(uistr::forceOnOff,"on",forceId);
    }

    if(forcetype == force_GROW)
    {
        string growthType = getNodeAttributeValue(forceElement,xmlstr::XML_ATTRTYPE_TYPE);
        if(!growthType.empty())
        {
            uiInterface.doSet(uistr::growthType,growthType);
        }
    }

    if(forcetype == force_LOGSELECTSTICK)
    {
        string stype(lamarcstrings::shortSSelectionName);
        uiInterface.doSet(uistr::selectType,stype);
    }

    if(forcetype == force_DIVERGENCE)
    {
        // Parse the population-tree hierarchy.
        TiXmlElement* popElement =
            singleRequiredChild(forceElement,xmlstr::XML_TAG_POPTREE);
        TiXmlNode * child = NULL;

        // Dreadful quick-n-dirty structures to make sure we have a legal tree.
        std::map<std::string,std::string> popToAnc;
        std::set<std::string> ancSet;
        std::map<std::string,std::string>::iterator mit;
        std::set<std::string>::iterator sit;

        while((child = popElement->IterateChildren(xmlstr::XML_TAG_EPOCH_BOUNDARY,child)))
        {
            TiXmlElement * boundaryElement = child->ToElement();
            TiXmlElement* newpopElement =
                singleRequiredChild(boundaryElement,xmlstr::XML_TAG_NEWPOP);
            StringVec1d newpops = getNodeTextSplitOnWhitespace(newpopElement);

            if (newpops.size() != 2)
            {
                // Throw wrong number of populations error.
                m_parser.ThrowDataError(xmlstr::XML_ERR_NEWPOP, newpopElement->Row());
            }

            // JNOTE: Not using the SetGet machinery because of obscure "unsolved problems".
            // Fix this right! at some point.
            uiInterface.GetCurrentVars().forces.AddNewPops(newpops);

            TiXmlElement* ancestorElement = singleRequiredChild(boundaryElement,xmlstr::XML_TAG_ANCESTOR);
            string ancname = getNodeText(ancestorElement);
            if(ancname.empty())
            {
                m_parser.ThrowDataError(xmlstr::XML_ERR_EMPTY_ANCESTOR, ancestorElement->Row());
            }
            else
            {
                // JNOTE: Not using the SetGet machinery because of obscure "unsolved problems".
                // Fix this right! at some point.
                // uiInterface.doSet(uistr::divergenceEpochAncestor,ancname);
                uiInterface.GetCurrentVars().forces.AddAncestor(ancname);
            }

            // Check newpops[0] not already a child.
            mit = popToAnc.find(newpops[0]);
            if(mit != popToAnc.end())
            {
                std::string oldAnc = (*mit).second;
                m_parser.ThrowDataError(xmlstr::XML_ERR_MULTIPLE_ANCESTORS_0
                                        + newpops[0]
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_1
                                        + oldAnc
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_2
                                        + ancname
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_3,
                                        ancestorElement->Row()
                    );
            }

            // Check newpops[1] not already a child.
            mit = popToAnc.find(newpops[1]);
            if(mit != popToAnc.end())
            {
                std::string oldAnc = (*mit).second;
                m_parser.ThrowDataError(xmlstr::XML_ERR_MULTIPLE_ANCESTORS_0
                                        + newpops[1]
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_1
                                        + oldAnc
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_2
                                        + ancname
                                        + xmlstr::XML_ERR_MULTIPLE_ANCESTORS_3,
                                        ancestorElement->Row()
                    );
            }

            // Check ancname not already used.
            sit = ancSet.find(ancname);
            if(sit != ancSet.end())
            {
                m_parser.ThrowDataError(xmlstr::XML_ERR_DUPLICATE_ANCESTOR_0
                                        + ancname
                                        + xmlstr::XML_ERR_DUPLICATE_ANCESTOR_1,
                                        ancestorElement->Row()
                    );

            }

            // Everything OK! Let's add data -- edit only one of the three at your peril.
            popToAnc[newpops[0]] = ancname;
            popToAnc[newpops[1]] = ancname;
            ancSet.insert(ancname);
        }

        // Now, check that we have the following:
        // * number of keys mapping pops to ancestors in (2*pops) - 2
        //   num pops + num internal nodes - one root
        // * exactly one ancestor not a key in map from pops to ancestors
        // * every pop name is a key in map from pops to ancestors
        // * no pop name a key in set of ancestors

        size_t numNotRoot = popToAnc.size();
        size_t numInternal = ancSet.size();

        std::set<std::string> popNamesSpecifiedInData;
        TiXmlElement * docElement = m_parser.GetRootElement();
        TiXmlElement * dataElem = singleRequiredChild(docElement,xmlstr::XML_TAG_DATA);

        TiXmlNode * regChild = NULL;
        while((regChild = dataElem->IterateChildren(xmlstr::XML_TAG_REGION,regChild)))
        {
            TiXmlElement * regionElement = regChild->ToElement();
            TiXmlNode * popChild = NULL;
            while((popChild = regionElement->IterateChildren(xmlstr::XML_TAG_POPULATION,popChild)))
            {
                TiXmlElement * populationElement = popChild->ToElement();
                std::string thisPopName = getNodeAttributeValue(populationElement,xmlstr::XML_ATTRTYPE_NAME);
                popNamesSpecifiedInData.insert(thisPopName);
            }
        }

        size_t popCount = popNamesSpecifiedInData.size();

        for(std::set<std::string>::const_iterator si = popNamesSpecifiedInData.begin(); si != popNamesSpecifiedInData.end(); si++)
        {
            std::string popName = *si;
            mit = popToAnc.find(popName);
            if (mit == popToAnc.end())
            {
                m_parser.ThrowDataError(xmlstr::XML_ERR_MISSING_NEWPOP_0
                                        + popName
                                        + xmlstr::XML_ERR_MISSING_NEWPOP_1,
                                        popElement->Row());
            }

            sit = ancSet.find(popName);
            if(sit != ancSet.end())
            {
                m_parser.ThrowDataError(xmlstr::XML_ERR_ANCESTOR_TRUEPOP_0
                                        + popName
                                        + xmlstr::XML_ERR_ANCESTOR_TRUEPOP_1,
                                        popElement->Row());
            }
        }

        if(numNotRoot != (popNamesSpecifiedInData.size() * 2) - 2 )
        {
            m_parser.ThrowDataError(xmlstr::XML_ERR_BAD_ANCESTOR_TREE,popElement->Row());
        }

        size_t nonRootAncestors = 0;
        for(sit = ancSet.begin(); sit != ancSet.end(); sit++)
        {
            mit = popToAnc.find(*sit);
            if (mit != popToAnc.end())
            {
                nonRootAncestors++;
            }
        }

        if(nonRootAncestors != ancSet.size() - 1)
        {
            m_parser.ThrowDataError(xmlstr::XML_ERR_BAD_ANCESTOR_TREE,popElement->Row());
        }

        // UGH -- this is awful -- we must change to bayesian and force
        // epoch arranger to be non-zero
        if(!uiInterface.doGetBool(uistr::bayesian))
        {
            // this should do a decent enough job of choosing a value for the arranger frequency
            uiInterface.doSet(uistr::bayesian,"true");
        }
        double epochFreq = uiInterface.doGetDouble(uistr::epochSizeArranger);
        if (epochFreq <= 0.0) {
           double bayesFreq = uiInterface.doGetDouble(uistr::bayesArranger);
           double resimFreq = uiInterface.doGetDouble(uistr::dropArranger);
           epochFreq = (bayesFreq > resimFreq) ? resimFreq : bayesFreq;
           uiInterface.doSet(uistr::epochSizeArranger,ToString(epochFreq));
        }



#if 0
        TiXmlElement* boundaryElement =
            singleRequiredChild(popElement,xmlstr::XML_TAG_EPOCH_BOUNDARY);
        do
        {
            TiXmlElement* newpopElement =
                singleRequiredChild(boundaryElement,xmlstr::XML_TAG_NEWPOP);
            StringVec1d newpops = getNodeTextSplitOnWhitespace(newpopElement);
            // JNOTE: not using the SetGet machinery because of obscure "unsolved problems"
            // fix this right! at some point
            uiInterface.GetCurrentVars().forces.AddNewPops(newpops);

            TiXmlElement* ancestorElement =
                singleRequiredChild(boundaryElement,xmlstr::XML_TAG_ANCESTOR);
            string ancname = getNodeText(ancestorElement);
            // JNOTE: not using the SetGet machinery because of obscure "unsolved problems"
            // fix this right! at some point
            uiInterface.GetCurrentVars().forces.AddAncestor(ancname);

            boundaryElement = singleOptionalChild(popElement,xmlstr::XML_TAG_EPOCH_BOUNDARY);
        } while (boundaryElement != NULL);
#endif
    }

    if(uiInterface.doGetBool(uistr::forceLegal,forceId))
    {
        DoOptionalElement(forceElement,xmlstr::XML_TAG_MAX_EVENTS,uistr::maxEvents,forceId);
        DoOptionalElement(forceElement,xmlstr::XML_TAG_DISEASELOCATION,uistr::diseaseLocation,forceId);

        //LS NOTE:  Order matters in this next section. First, we do profiles,
        // because if they're done after the groups, the 'none' entries
        // override any (correct) 'on' entries for grouped parameters.  Next
        // we do constraints, then groups, because the groups tags need
        // to override the constraints tags.
        //
        // Then we do the priors, because they will be affected by the groups
        // and constraints.
        //
        // Then we do start values, because they will be affected by the
        // constraints, groups, priors, and methods.  Sadly, we have to do a bit
        // of munging in DoStartValuesAndMethods because the Methods are, in
        // turn, affected by the process of setting the start values.
        DoProfiles(forceElement,forceId);
        DoConstraints(forceElement,forceId);
        DoGroups(forceElement, forceId);
        DoPriors(forceElement, forceId);
        DoStartValuesAndMethods(forceElement,forceId);
        DoTrueValues(forceElement,forceId);

        // LS DEBUG -- we can at least check the zeroes for validity.
        //  Eventually the method to do this will change, so don't copy this
        //  technique elsewhere.
        if (!uiInterface.GetCurrentVars().forces.GetForceZeroesValidity(forcetype))
        {
            string err = "Invalid settings for force ";
            err += ToString(forcetype) + ".  Too many parameters are set invalid or have a start value of 0.0.";
            throw data_error(err);
        }
        uiInterface.GetCurrentVars().forces.FixGroups(forcetype);
    }
}

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

void
ParseTreeToSettings::DoStartValuesAndMethods(TiXmlElement* forceElement, UIId forceId)
{
    TiXmlElement * methodsElement =
        singleOptionalChild(forceElement,xmlstr::XML_TAG_METHOD);
    TiXmlElement * startValuesElement =
        singleOptionalChild(forceElement,xmlstr::XML_TAG_START_VALUES);

    if(methodsElement == NULL && startValuesElement == NULL)
        // simply return and take the default values
    {
        return;
    }

    force_type thisForce = forceId.GetForceType();
    long expectedNumParameters =
        uiInterface.GetCurrentVars().forces.GetNumParameters(thisForce);
    StringVec1d values;
    StringVec1d methods;

    if(startValuesElement != NULL)
    {
        values = getNodeTextSplitOnWhitespace(startValuesElement);
        if(static_cast<long>(values.size()) != expectedNumParameters)
        {
            m_parser.ThrowDataError(xmlstr::XML_ERR_START_VALUE_COUNT_0
                                    + ToString(expectedNumParameters)
                                    + xmlstr::XML_ERR_START_VALUE_COUNT_1
                                    + ToString(thisForce)
                                    + xmlstr::XML_ERR_START_VALUE_COUNT_2
                                    + ToString(values.size())
                                    + xmlstr::XML_ERR_START_VALUE_COUNT_3,
                                    startValuesElement->Row());
        }
    }

    if(methodsElement != NULL)
    {
        methods = getNodeTextSplitOnWhitespace(methodsElement);
        if(static_cast<long>(methods.size()) != expectedNumParameters)
        {
            m_parser.ThrowDataError(xmlstr::XML_ERR_METHOD_TYPE_COUNT_0
                                    + ToString(expectedNumParameters)
                                    + xmlstr::XML_ERR_METHOD_TYPE_COUNT_1
                                    + ToString(thisForce)
                                    + xmlstr::XML_ERR_METHOD_TYPE_COUNT_2
                                    + ToString(methods.size())
                                    + xmlstr::XML_ERR_METHOD_TYPE_COUNT_2,
                                    methodsElement->Row());
        }
    }

    // at this point, we either have a full set of method types or none
    // and we have either a full set of start values or none

    if(methodsElement == NULL)
        // an easy case -- all methods become type USER
    {
        for(long index=0; index < (long)values.size(); index++)
        {
            UIId id(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::startValue,values[index],id);
            uiInterface.doSet(uistr::startValueMethod,ToString(method_USER),id);
        }
        return;
    }

    if(startValuesElement == NULL)
        // OK as long as method isn't USER
    {
        for(long index=0; index < (long)methods.size(); index++)
        {
            UIId id(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::startValueMethod,methods[index],id);

            if(StringMatchesMethodType(methods[index],method_USER))
            {
                double defaultStartVal
                    = uiInterface.GetCurrentVars().forces.GetStartValue(forceId.GetForceType(),index);
                uiInterface.AddWarning(xmlstr::XML_ERR_METHOD_USER_WITHOUT_VALUE_0
                                       +ToString(forceId.GetForceType())
                                       +xmlstr::XML_ERR_METHOD_USER_WITHOUT_VALUE_1
                                       +ToString(defaultStartVal)
                                       +xmlstr::XML_ERR_METHOD_USER_WITHOUT_VALUE_2);
            }
        }
        return;
    }

    // OK. Now we should have a full set of method types and start values
    assert((long)methods.size() == expectedNumParameters);
    assert((long)values.size() == expectedNumParameters);

    for(long index=0; index < (long)values.size(); index++)
    {
        UIId id(forceId.GetForceType(),index);
        uiInterface.doSet(uistr::startValue,values[index],id);
    }
    DoubleVec1d usersStartValues = uiInterface.GetCurrentVars().forces.GetStartValues(forceId.GetForceType());

    for(long index=0; index < (long)values.size(); index++)
    {
        UIId id(forceId.GetForceType(),index);
        uiInterface.doSet(uistr::startValueMethod,methods[index],id);
    }
    DoubleVec1d overriddenStartValues = uiInterface.GetCurrentVars().forces.GetStartValues(forceId.GetForceType());

    assert(usersStartValues.size() == overriddenStartValues.size());
    assert(usersStartValues.size() == methods.size());

    for(long index=0; index < (long)methods.size(); index++)
    {
        double userVal = usersStartValues[index];
        double overriddenValue = overriddenStartValues[index];

        if (fabs(userVal - overriddenValue) > 0.00001)
        {
            method_type culprit
                = uiInterface.doGetMethodType(uistr::startValueMethod,UIId(thisForce,index));
            uiInterface.AddWarning("Warning:  setting the start method for a "
                                   + ToString(forceId.GetForceType())
                                   + " parameter to "
                                   + ToString(culprit, true)
                                   + " will override the value set in the <"
                                   + xmlstr::XML_TAG_START_VALUES
                                   + "> tag.");
        }
    }

#if 0
    bool someStartValues = false;
    // set start values first since these calls set methods
    if(startValuesElement != NULL)
    {
        someStartValues = true;
        StringVec1d values = getNodeTextSplitOnWhitespace(startValuesElement);
        for(long index=0; index < (long)values.size(); index++)
        {
            UIId id(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::startValue,values[index],id);
        }
        if(values.empty())
        {
            someStartValues = false;
            uiInterface.AddWarning("Warning:  empty <" + xmlstr::XML_TAG_START_VALUES + "> tag found; using defaults.");
        }

    }
    else
    {
        uiInterface.AddWarning("Warning:  missing <" + xmlstr::XML_TAG_START_VALUES + "> tag ; using defaults.");
    }

    DoubleVec1d originalStartValues = uiInterface.GetCurrentVars().forces.GetStartValues(forceId.GetForceType());
    if(methodsElement != NULL)
    {
        StringVec1d methods = getNodeTextSplitOnWhitespace(methodsElement);
        for(long index=0; index < (long)methods.size(); index++)
        {
            UIId id(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::startValueMethod,methods[index],id);
        }
        if(methods.empty())
        {
            uiInterface.AddWarning("Warning:  empty <" + xmlstr::XML_TAG_METHOD + "> tag found; using defaults");
        }
        else
        {
            if (someStartValues)
            {
                DoubleVec1d newStartValues = uiInterface.GetCurrentVars().forces.GetStartValues(forceId.GetForceType());
                for (unsigned long i=0; i<originalStartValues.size(); i++)
                {
                    if (fabs(originalStartValues[i] - newStartValues[i]) > 0.00001)
                    {
                        method_type culprit = uiInterface.doGetMethodType(uistr::startValueMethod,UIId(forceId.GetForceType(), i));
                        uiInterface.AddWarning("Warning:  setting the start method for a "
                                               + ToString(forceId.GetForceType())
                                               + " parameter to "
                                               + ToString(culprit, true)
                                               + " will override the value set in the <"
                                               + xmlstr::XML_TAG_START_VALUES + "> tag.");
                    }
                }
            }
        }
    }
#endif
}

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

void
ParseTreeToSettings::DoTrueValues(TiXmlElement* forceElement, UIId forceId)
{
    TiXmlElement * trueValuesElement =
        singleOptionalChild(forceElement,xmlstr::XML_TAG_TRUEVALUE);
    if(trueValuesElement != NULL)
    {
        StringVec1d values = getNodeTextSplitOnWhitespace(trueValuesElement);
        for(unsigned long index=0; index < values.size(); index++)
        {
            UIId id(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::trueValue,values[index],id);
        }
        if(values.empty())
        {
            uiInterface.AddWarning("Warning:  empty <" + xmlstr::XML_TAG_TRUEVALUE + "> tag found; using defaults");
        }
    }
}

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

void
ParseTreeToSettings::DoProfiles(TiXmlElement * forceElement, UIId forceId)
{
    TiXmlElement * profilesElement =
        singleOptionalChild(forceElement,xmlstr::XML_TAG_PROFILES);

    if(profilesElement != NULL)
    {
        string profilesString = getNodeText(profilesElement);
        ProftypeVec1d profiles = ProduceProftypeVec1dOrBarf(profilesString);
        size_t index;
        bool hasFix = false;
        bool hasPerc= false;
        for(index=0; index < profiles.size(); index++)
        {
            UIId localId(forceId.GetForceType(),index);
            switch(profiles[index])
            {
                case profile_PERCENTILE:
                    hasPerc = true;
                    uiInterface.doSet(uistr::profileByID,"true",localId);
                    break;
                case profile_FIX:
                    hasFix = true;
                    uiInterface.doSet(uistr::profileByID,"true",localId);
                    break;
                case profile_NONE:
                    uiInterface.doSet(uistr::profileByID,"false",localId);
                    break;
            }
        }
        if(hasFix && !hasPerc)
        {
            uiInterface.doSet(uistr::profileByForce,"fixed",forceId);
        }
        else
        {
            uiInterface.doSet(uistr::profileByForce,"percentile",forceId);
            if(hasFix)
            {
                // add warning
                std::string errstring("Warning:  <profiles> element near line ");
                errstring += ToString(profilesElement->Row());
                errstring += " contains both fixed and percentile profiling.";
                errstring += " We only allow one type per force and are";
                errstring += " changing the type to percentile profiling.";
                uiInterface.AddWarning(errstring);
            }
        }
    }
}

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

void
ParseTreeToSettings::DoConstraints(TiXmlElement * forceElement, UIId forceId)
{
    TiXmlElement * constraintsElement =
        singleOptionalChild(forceElement,xmlstr::XML_TAG_CONSTRAINTS);

    if(constraintsElement != NULL)
    {
        string constraintsString = getNodeText(constraintsElement);
        vector < ParamStatus > constraints
            = ProduceParamstatusVec1dOrBarf(constraintsString);
        size_t index;
        for(index=0; index < constraints.size(); index++)
        {
            UIId localID(forceId.GetForceType(),index);
            uiInterface.doSet(uistr::constraintType,ToString(constraints[index].Status()) ,localID);
        }
    }
}

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

void
ParseTreeToSettings::DoGroups(TiXmlElement * forceElement, UIId forceId)
{
    TiXmlNode * child = NULL;
    size_t index = 0;
    while((child = forceElement->IterateChildren(xmlstr::XML_TAG_GROUP,child)))
    {
        TiXmlElement * groupElement = child->ToElement();
        DoGroup(groupElement, forceId, index);
        index++;
    }
}

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

void
ParseTreeToSettings::DoGroup(TiXmlElement * groupElement, UIId forceId,
                             size_t index)
{
    string constraintType
        = getNodeAttributeValue(groupElement,xmlstr::XML_ATTRTYPE_CONSTRAINT);
    string indicesString = getNodeText(groupElement);

    UIId localID(forceId.GetForceType(),index);
    uiInterface.doSet(uistr::groupParamList, indicesString, localID);
    uiInterface.doSet(uistr::groupConstraintType, constraintType, localID);
}

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

void
ParseTreeToSettings::DoPriors(TiXmlElement * forceElement, UIId forceId)
{
    TiXmlNode * child = NULL;
    while((child = forceElement->IterateChildren(xmlstr::XML_TAG_PRIOR,child)))
    {
        TiXmlElement * priorElement = child->ToElement();
        DoPrior(priorElement, forceId);
    }
}

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

void
ParseTreeToSettings::DoPrior(TiXmlElement * priorElement, UIId forceId)
{
    TiXmlElement * parameterElement =
        singleOptionalChild(priorElement,xmlstr::XML_TAG_PARAMINDEX);

    long paramID = uiconst::GLOBAL_ID;

    if (parameterElement)
    {
        string paramName = getNodeText(parameterElement);
        try
        {
            paramID = ProduceLongOrBarf(paramName);
            paramID--;
        }
        catch (const data_error& e)
        {
            if ((!CaselessStrCmp(paramName, uistr::defaultStr)) && (!CaselessStrCmp(paramName, uistr::allStr)))
            {
                m_parser.ThrowDataError("Parameter index must be an integer or 'all'", priorElement->Row());
            }
            paramID = uiconst::GLOBAL_ID;
        }
    }
    UIId localId(forceId.GetForceType(), paramID);

    string priorTypeString
        = getNodeAttributeValue(priorElement,xmlstr::XML_ATTRTYPE_TYPE);
    uiInterface.doSet(uistr::priorType, priorTypeString, localId);

    try
    {
        TiXmlElement * lowerBoundElement =
            singleRequiredChild(priorElement,xmlstr::XML_TAG_PRIORLOWERBOUND);
        string lowerString = getNodeText(lowerBoundElement);
        uiInterface.doSet(uistr::priorLowerBound, lowerString, localId);

        TiXmlElement * upperBoundElement =
            singleRequiredChild(priorElement,xmlstr::XML_TAG_PRIORUPPERBOUND);
        string upperString = getNodeText(upperBoundElement);
        uiInterface.doSet(uistr::priorUpperBound, upperString, localId);
    }
    catch (const data_error&)
    {
        //Presumably, the lower bound was higher than the *default* upper
        // bound--try them in the reverse order instead.
        TiXmlElement * upperBoundElement =
            singleRequiredChild(priorElement,xmlstr::XML_TAG_PRIORUPPERBOUND);
        string upperString = getNodeText(upperBoundElement);
        uiInterface.doSet(uistr::priorUpperBound, upperString, localId);

        TiXmlElement * lowerBoundElement =
            singleRequiredChild(priorElement,xmlstr::XML_TAG_PRIORLOWERBOUND);
        string lowerString = getNodeText(lowerBoundElement);
        uiInterface.doSet(uistr::priorLowerBound, lowerString, localId);

    }

#ifdef LAMARC_NEW_FEATURE_RELATIVE_SAMPLING
    TiXmlElement * sampleRateElement =
        singleOptionalChild(priorElement,xmlstr::XML_TAG_RELATIVE_SAMPLE_RATE);
    if (sampleRateElement)
    {
        string rateString = getNodeText(sampleRateElement);
        uiInterface.doSet(uistr::relativeSampleRate,rateString,localId);
    }
#endif
}

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

void ParseTreeToSettings::DoTraits(TiXmlElement * dataElement)
{
    TiXmlNode * child = NULL;

    long regionNumber = 0;

    while((child = dataElement->IterateChildren(xmlstr::XML_TAG_REGION,child)))
    {
        TiXmlElement * traitsElement = singleOptionalChild(child->ToElement(), xmlstr::XML_TAG_TRAITS);

        DoRegionTraits(traitsElement,regionNumber);
        regionNumber++;
    }
}

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

void ParseTreeToSettings::DoRegionTraits(TiXmlElement * traitsElement, long regionNumber)
{
    if (traitsElement != NULL)
    {
        TiXmlNode * traitNode = NULL;
        while((traitNode = traitsElement->IterateChildren(xmlstr::XML_TAG_TRAIT,traitNode)))
        {
            TiXmlElement * traitElement = traitNode->ToElement();
            DoRegionTrait(traitElement, regionNumber);
        }
    }
}

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

void ParseTreeToSettings::DoRegionTrait(TiXmlElement * traitElement, long regionNumber)
{
    TiXmlElement * nameElement = singleRequiredChild(traitElement, xmlstr::XML_TAG_NAME);
    string tname = getNodeText(nameElement);
    if (!uiInterface.GetCurrentVars().datapackplus.HasLocus(regionNumber, tname))
    {
        throw data_error("Error:  trait settings found for '"
                         + tname
                         + "', but no data was found for this trait.  Check spelling, or delete these settings.");
    }
    long locusNumber = uiInterface.GetCurrentVars().datapackplus.GetLocusIndex(regionNumber, tname);

    TiXmlElement * modelElement = singleOptionalChild(traitElement, xmlstr::XML_TAG_MODEL);
    if (modelElement != NULL)
    {
        DoDLModel(modelElement,regionNumber,locusNumber);
    }

    TiXmlElement * locationsElement = singleOptionalChild(traitElement, xmlstr::XML_TAG_POSSIBLE_LOCATIONS);
    if (locationsElement != NULL)
    {
        DoLocations(locationsElement, regionNumber, locusNumber);
    }

    // Do this after setting the range (locations) so we can warn/throw appropriately
    TiXmlElement * analysisElement = singleOptionalChild(traitElement, xmlstr::XML_TAG_ANALYSIS);
    if (analysisElement != NULL)
    {
        DoAnalysis(analysisElement, regionNumber, locusNumber);
    }

    TiXmlElement * phenotypesElement = singleOptionalChild(traitElement, xmlstr::XML_TAG_PHENOTYPES);
    if (phenotypesElement != NULL)
    {
        DoPhenotypes(phenotypesElement, regionNumber, locusNumber);
    }
}

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

void ParseTreeToSettings::DoLocations(TiXmlElement* locationsElement, long regionNumber, long locusNumber)
{
    UIRegId locId(regionNumber,locusNumber, uiInterface.GetCurrentVars());
    rangeset rset;

    //Collect the whole range, then set it afterwards.
    TiXmlNode* rangeNode = NULL;
    while ((rangeNode = locationsElement->IterateChildren(xmlstr::XML_TAG_RANGE, rangeNode)))
    {
        TiXmlElement * rangeElement = rangeNode->ToElement();
        TiXmlElement * startElement = singleRequiredChild(rangeElement, xmlstr::XML_TAG_START);
        TiXmlElement * endElement   = singleRequiredChild(rangeElement, xmlstr::XML_TAG_END);
        long start = ProduceLongOrBarf(getNodeText(startElement));
        long end   = ProduceLongOrBarf(getNodeText(endElement));
        rangepair range = std::make_pair(start, end);
        range.second++;
        rset = AddPairToRange(range, rset);
    }
    uiInterface.GetCurrentVars().traitmodels.SetRange(locId, rset);
}

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

void ParseTreeToSettings::DoAnalysis(TiXmlElement* analysisElement, long regionNumber, long locusNumber)
{
    UIRegId locId(regionNumber,locusNumber, uiInterface.GetCurrentVars());
    string analysisString = getNodeText(analysisElement);
    mloc_type analysis = ProduceMlocTypeOrBarf(analysisString);
    uiInterface.GetCurrentVars().traitmodels.SetAnalysisType(locId, analysis);
}

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

void ParseTreeToSettings::DoPhenotypes(TiXmlElement* phenotypesElement, long regionNumber, long locusNumber)
{
    StringVec2d alleles = uiInterface.GetCurrentVars().datapackplus.GetUniqueAlleles(regionNumber, locusNumber);
    size_t nalleles = alleles[0].size(); //Assumes only one marker

    std::set<long> ploidies = uiInterface.GetCurrentVars().datapackplus.GetPloidies(regionNumber);
    //Now calculate how many genotypes we need.  For a given number
    // of alleles A, and ploidy P, we need:
    //
    //  (A + P - 1)!
    //  ------------
    //   P!(A - 1)!
    //
    // which is the formula for 'combination with repetition'.  See, for example
    // http://en.wikipedia.org/wiki/Combinatorics
    //
    // That's gotta be some sort of milestone--a URL in a comment!  Wow.
    //
    // At any rate, we need to calculate this formula for each possible ploidy,
    // and add them up.  Normally, there'd be only one, but there are exceptions
    // like if our region is on the X chromosome.

    size_t targetCombs = 0;
    for (std::set<long>::iterator ploid = ploidies.begin(); ploid != ploidies.end(); ploid++)
    {
        targetCombs += static_cast<long>(factorial(nalleles + (*ploid) - 1)) /
            (static_cast<long>(factorial(*ploid)) * static_cast<long>(factorial(nalleles - 1)));
    }

    vector<TiXmlElement *> genotypeElements = getAllOptionalDescendantElements(phenotypesElement, xmlstr::XML_TAG_GENOTYPE);
    if (genotypeElements.size() < targetCombs)
    {
        string msg = "Error:  not enough genotypes listed in your phenotype list.  You must provide a set of phenotypes"
            " for every possible combination of alleles (for which you have "
            + ToString(nalleles) + ")";
        if (ploidies.size() > 1)
        {
            msg += ".  Also note that you have "
                + ToString(ploidies.size())
                + " different ploidies in your list of individuals, meaning that you must provide a complete"
                " list of combinations of alleles for all different possible numbers of genes, too.";
        }
        else
        {
            msg += ", for the ploidy of your individuals (which are all " + ToString(*ploidies.begin()) + ".)";
        }
        msg += "  All told, you should have "
            + ToString(targetCombs)
            + " genotypes, while you currently have "
            + ToString(genotypeElements.size()) + ".";

        throw data_error(msg);
    }
    if (genotypeElements.size() > targetCombs)
    {
        string msg = "Error:  You have provided too many genotypes for the number of alleles for your trait ("
            + ToString(nalleles)
            + ") for the ploidies of your sampled individuals.  You should have "
            + ToString(targetCombs)
            + ", but currently have "
            + ToString(genotypeElements.size())
            + ".  Remember that order doesn't matter for genotypes--being heterozygous should give you the same phenotype regardless of which allele is which.";

        throw data_error(msg);
    }

    for (vector<TiXmlElement* >::iterator genotypeElement = genotypeElements.begin(); genotypeElement != genotypeElements.end(); genotypeElement++)
    {
        //Get the set of alleles.
        TiXmlElement* alleleElement = singleRequiredChild(*genotypeElement, xmlstr::XML_TAG_ALLELES);
        StringVec1d alleles = getNodeTextSplitOnWhitespace(alleleElement);

        //Get the (potentially many) associated phenotypes.
        TiXmlNode * phenotypeNode = NULL;
        while ((phenotypeNode = (*genotypeElement)->IterateChildren(xmlstr::XML_TAG_PHENOTYPE, phenotypeNode)))
        {
            TiXmlElement* phenotypeElement = phenotypeNode->ToElement();
            TiXmlElement* phenNameElement = singleRequiredChild(phenotypeElement, xmlstr::XML_TAG_PHENOTYPE_NAME);
            TiXmlElement* penetranceElement = singleRequiredChild(phenotypeElement, xmlstr::XML_TAG_PENETRANCE);

            string phenName = getNodeText(phenNameElement);
            double penetrance = ProduceDoubleOrBarf(getNodeText(penetranceElement));
            UIRegId locId(regionNumber,locusNumber, uiInterface.GetCurrentVars());
            uiInterface.GetCurrentVars().traitmodels.AddPhenotype(locId, alleles, phenName, penetrance);
        }
    }
}

//____________________________________________________________________________________
