// $Id: registry.cpp,v 1.104 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 "local_build.h"

#include "arranger.h"
#include "analyzer.h"
#include "bayesanalyzer_1d.h"
#include "defaults.h"
#include "dlmodel.h"
#include "errhandling.h"
#include "force.h"
#include "likelihood.h"
#include "maximizer.h"
#include "region.h"
#include "regiongammainfo.h"
#include "registry.h"
#include "runreport.h"
#include "tree.h"
#include "treesum.h"
#include "ui_interface.h"
#include "ui_regid.h"
#include "ui_vars.h"
#include "cellmanager.h"

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

using namespace std;

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

class RegionGammaInfo;

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

Registry::Registry()
    :
    protoTree(NULL),
    protoTreeSummary(NULL),
    chainparams(NULL),
    userparams(NULL),
    forcesummary(NULL),
    random(NULL),
    runreport(NULL),
    maximizer(NULL),
    PLsingle(NULL),
    PLreplicate(NULL),
    PLregion(NULL),
    PLgammaRegion(NULL),
    pRegionGammaForceInfo(NULL),
    bayesanalyzer(NULL),
    analyzer(NULL),
    m_currRegion(-1),
    m_ARGfound(false),
    m_convert_output_to_eliminate_zeroes(defaults::convert_output_to_eliminate_zeroes)
#ifdef LAMARC_QA_SINGLE_DENOVOS
    ,
    m_denovoTreeRejectCount(0),
    m_denovoMaxRejectCount(0),
    m_lastDenovoGood(false),
    m_defaultBranchCount(0),
    m_denovoCount(defaults::numDenovos)
#endif // LAMARC_QA_SINGLE_DENOVOS
{
    cellmanager = new CellManager;
} // Registry constructor

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

Registry::~Registry()
{
    delete protoTree;
    delete protoTreeSummary;
    delete PLsingle;
    delete PLreplicate;
    delete PLregion;
    delete PLgammaRegion;
    delete runreport;
    delete maximizer;
    delete bayesanalyzer;
    delete analyzer;
    delete chainparams;
    delete userparams;
    delete random;
    delete forcesummary;
    delete pRegionGammaForceInfo;
} // Registry destructor

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

const Tree& Registry::GetProtoTree() const
{
    if (protoTree == NULL) ThrowBadPrototype();
    return(*protoTree);
} // GetProtoTree

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

const TreeSummary& Registry::GetProtoTreeSummary() const
{
    if (protoTreeSummary == NULL) ThrowBadPrototype();
    return (*protoTreeSummary);
} // GetProtoTreeSummary

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

RunReport& Registry::GetRunReport()
{
    if (runreport == NULL) ThrowBadPrototype();
    return (*runreport);
} // Registry::GetRunReport()

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

const RunReport& Registry::GetRunReport() const
{
    if (runreport == NULL) ThrowBadPrototype();
    return (*runreport);
} // Registry::GetRunReport()

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

Maximizer& Registry::GetMaximizer()
{
    if (maximizer == NULL) ThrowBadPrototype();
    return (*maximizer);
} // Registry::GetMaximizer

Maximizer * Registry::GetMaximizerPtr()
{
    if (maximizer == NULL) ThrowBadPrototype();
    return (maximizer);
} // Registry::GetMaximizer

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

BayesAnalyzer_1D& Registry::GetBayesAnalyzer_1D()
{
    if (bayesanalyzer == NULL) ThrowBadPrototype();
    return (*bayesanalyzer);
} // Registry::GetBayesAnalyzer_1D

BayesAnalyzer_1D * Registry::GetBayesAnalyzer_1DPtr()
{
    if (bayesanalyzer == NULL) ThrowBadPrototype();
    return (bayesanalyzer);
} // Registry::GetBayesAnalyzer_1D

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

SinglePostLike& Registry::GetSinglePostLike()
{
    if (PLsingle == NULL) ThrowBadPrototype();
    return (*PLsingle);
} // Registry::GetSinglePostLike

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

ReplicatePostLike& Registry::GetReplicatePostLike()
{
    if (PLreplicate == NULL) ThrowBadPrototype();
    return (*PLreplicate);
} // Registry::GetReplicatePostLike

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

RegionPostLike& Registry::GetRegionPostLike()
{
    if (GetRegionGammaInfo())
    {
        if (PLgammaRegion == NULL) ThrowBadPrototype();
        return (*PLgammaRegion);
    }
    else
    {
        if (PLregion == NULL) ThrowBadPrototype();
        return (*PLregion);
    }
} // Registry::GetRegionPostLike

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

Analyzer& Registry::GetAnalyzer()
{
    if (analyzer == NULL) ThrowBadPrototype();
    return (*analyzer);
} // Registry::GetRegionPostLike

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

void Registry::Register(Tree* tree)
{
    delete protoTree;
    protoTree = tree;
} // Register (Tree)

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

void Registry::Register(RunReport* report)
{
    delete runreport;
    runreport = report;

} // Registry::Register(RunReport)

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

void Registry::Register(Maximizer* maxim)
{
    delete maximizer;
    maximizer = maxim;
} // Registry::Register (Maximizer)

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

void Registry::Register(BayesAnalyzer_1D* bayesan)
{
    delete bayesanalyzer;
    bayesanalyzer = bayesan;
} // Registry::Register (BayesAnalyzer_1D)

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

void Registry::Register(SinglePostLike* singlel)
{
    delete PLsingle;
    PLsingle = singlel;
} // Registry::Register (SinglePostLike)

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

void Registry::Register(ReplicatePostLike* replicate)
{
    delete PLreplicate;
    PLreplicate = replicate;
} // Registry::Register (ReplicatePostLike)

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

void Registry::Register(RegionPostLike* region)
{
    delete PLregion;
    PLregion = region;
} // Registry::Register (RegionPostLike)

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

void Registry::Register(GammaRegionPostLike* gammaRegion)
{
    delete PLgammaRegion;
    PLgammaRegion = gammaRegion;
} // Registry::Register (GammaRegionPostLike)

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

void Registry::Register(RegionGammaInfo* pRGFI)
{
    delete pRegionGammaForceInfo;
    pRegionGammaForceInfo = pRGFI;
} // Registry::Register (RegionGammaInfo)

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

void Registry::Register(Analyzer *thisanalyzer)
{
    delete analyzer;
    analyzer = thisanalyzer;
} // Registry::Register (RegionPostLike)

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

void Registry::Register(TreeSummary* treesum)
{
    delete protoTreeSummary;
    protoTreeSummary = treesum;
} // Registry::Register (intervaldata)

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

void Registry::SetCurrentRegionIndex(long regionIndex)
{
    assert( regionIndex >= 0);
    assert( regionIndex < datapack.GetNRegions());
    m_currRegion = regionIndex;
}

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

long Registry::GetCurrentReclocOffset() const
{
    assert( m_currRegion >= 0);
    assert( m_currRegion < datapack.GetNRegions());
    const Region& reg = datapack.GetRegion(m_currRegion);
    long globalStart = reg.GetGlobalMapPosition(0);
    return globalStart;
}

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

void Registry::ThrowBadPrototype() const
{
    logic_error e("Attempt to use unregistered prototype");
    throw e;
}

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

void Registry::FinalizeDataPack(UIInterface& ui)
{
    //Set the effective population sizes
    DoubleVec1d effectivePopSizes
        = ui.GetCurrentVars().datapackplus.GetEffectivePopSizes();
    long nRegions = datapack.GetNRegions();
    assert(static_cast<long>(effectivePopSizes.size()) == nRegions);
    for (long regnum=0; regnum < nRegions; regnum++)
    {
        datapack.GetRegion(regnum).SetEffectivePopSize(effectivePopSizes[regnum]);
    }

    //Now set up all the trait information and whether to simulate
    for (long regnum=0; regnum < nRegions; regnum++)
    {
        long nLoci = datapack.GetRegion(regnum).GetNloci();
        long regoffset = datapack.GetRegion(regnum).GetSiteSpan().first;
        for (long locusnum=0; locusnum < nLoci; locusnum++)
        {
            Locus& locus = datapack.GetRegion(regnum).GetLocus(locusnum);
            locus.SetShouldSimulate(ui.GetCurrentVars().datapackplus.GetSimulateData(regnum, locusnum));
            if (locus.IsMovable())
            {
                UIRegId regID(regnum, locusnum, ui.GetCurrentVars());
                const UIVarsTraitModels& traits = ui.GetCurrentVars().traitmodels;
                locus.SetName(traits.GetName(regID));
                locus.SetAnalysisType(traits.GetAnalysisType(regID));
                locus.SetAllowedRange(traits.GetRange(regID), regoffset);
                locus.SetGlobalMapPosition(traits.GetInitialMapPosition(regID));
                //And set up the phenotypes (for simulation, possibly)
                locus.SetPhenotypes(traits.GetPhenotypes(regID));
            }
        }
        // This call sets up the moving and non-moving loci, gets them
        // assigned to their correct positions on the overall map of the
        // region, and creates their DLCells and DLCalculators.
        datapack.GetRegion(regnum).SetupAndMoveAllLoci();
    }
  // If the user asked for recombination and at least one Region lacks
  // the needed variable sites, abend now
  for (long regnum=0; regnum < nRegions; regnum++) {
    if (forcesummary->CheckForce(force_REC)) {
      bool okay = datapack.GetRegion(regnum).RecombinationCanBeEstimated();
      if (!okay) {
        incorrect_data e("***ERROR:  2+ variable sites in every region required to use recombination");
        throw e;
      }
    }
  }
  

} // FinalizeDataPack

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

void Registry::InstallChainParameters(UIInterface& ui)
{
    UIVarsChainParameters & chains = ui.GetCurrentVars().chains;

    double dropTiming = chains.GetDropArrangerRelativeTiming();
    double sizeTiming = chains.GetSizeArrangerRelativeTiming();
    double stairTiming = chains.GetStairArrangerRelativeTiming();
    double hapTiming =
        chains.GetHaplotypeArrangerPossible()
        ? chains.GetHaplotypeArrangerRelativeTiming()
        : 0.0 ;
    double probhapTiming =
        chains.GetProbHapArrangerPossible()
        ? chains.GetProbHapArrangerRelativeTiming()
        : 0.0 ;
    double bayesTiming =
        chains.GetDoBayesianAnalysis()
        ? chains.GetBayesianArrangerRelativeTiming()
        : 0.0 ;
    double locusTiming =
        ui.GetCurrentVars().traitmodels.AnyJumpingAnalyses()
        ? chains.GetLocusArrangerRelativeTiming()
        : 0.0 ;
    double zilchTiming =
        ui.GetCurrentVars().datapackplus.AnySimulation()
        ? chains.GetZilchArrangerRelativeTiming()
        : 0.0 ;
    double epochsizeTiming =
        chains.GetDoBayesianAnalysis()
        ? chains.GetEpochSizeArrangerRelativeTiming()
        : 0.0 ;

    chainparams = new ChainParameters(
        chains.GetChainTemperatures(),
        chains.GetTemperatureInterval(),
        chains.GetAdaptiveTemperatures(),
        chains.GetInitialNumberOfChains(),
        chains.GetInitialNumberOfSamples(),
        chains.GetInitialChainSamplingInterval(),
        chains.GetInitialNumberOfChainsToDiscard(),
        chains.GetFinalNumberOfChains(),
        chains.GetFinalNumberOfSamples(),
        chains.GetFinalChainSamplingInterval(),
        chains.GetFinalNumberOfChainsToDiscard(),
        chains.GetNumberOfReplicates(),
        chains.GetDoBayesianAnalysis(),
        dropTiming,
        sizeTiming,
        hapTiming,
        probhapTiming,
        bayesTiming,
        locusTiming,
        zilchTiming,
        stairTiming,
        epochsizeTiming
        );
}

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

void Registry::InstallUserParameters(UIInterface& ui)
{
    UIVarsUserParameters & uparams = ui.GetCurrentVars().userparams;

    userparams = new UserParameters(
        uparams.GetCurveFilePrefix(),
        uparams.GetMapFilePrefix(),
        uparams.GetReclocFilePrefix(),
        uparams.GetTraceFilePrefix(),
        uparams.GetNewickTreeFilePrefix(),
#ifdef LAMARC_QA_TREE_DUMP
        uparams.GetArgFilePrefix(),
#endif // LAMARC_QA_TREE_DUMP
        uparams.GetDataFileName(),
        uparams.GetProfilePrefix(),
        uparams.GetResultsFileName(),
        uparams.GetTreeSumInFileName(),
        uparams.GetTreeSumOutFileName(),
        uparams.GetXMLOutFileName(),
        uparams.GetXMLReportFileName(),
        uparams.GetVerbosity(),
        uparams.GetProgress(),
        uparams.GetPlotPost(),
        uparams.GetUseSystemClock(),
        uparams.GetReadSumFile(),
        uparams.GetWriteSumFile(),
        uparams.GetWriteCurveFiles(),
        uparams.GetWriteReclocFiles(),
        uparams.GetWriteTraceFiles(),
        uparams.GetWriteNewickTreeFiles(),
#ifdef LAMARC_QA_TREE_DUMP
        uparams.GetWriteArgFiles(),
        uparams.GetWriteManyArgs(),
#endif // LAMARC_QA_TREE_DUMP
        uparams.GetRandomSeed(),
        uparams.GetProgramStartTime()
        );

    random = new Random(uparams.GetRandomSeed());
}

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

DataModel * Registry::CreateDataModel(UIVarsDataModels& modelVars, const Locus& locus,
                                      const UIVarsDataPackPlus& dataPackPlus, UIRegId regionId)
{
    data_type thisDataType = locus.GetDataType();
    model_type modelType = modelVars.GetDataModelType(regionId);
    if(!(ModelTypeAcceptsDataType(modelType,thisDataType)))
    {
        // If we reach this point, we have a data model for an inappropriate
        // region/locus pair.  This should be avoided.  --LS NOTE
        assert(false);
        modelType = DefaultModelForDataType(thisDataType);
    }

    switch(modelType)
    {
        case F84:
        {
            F84Model * model
                = new F84Model(
                    locus.GetNmarkers(),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId),
                    modelVars.GetFrequencyA(regionId),
                    modelVars.GetFrequencyC(regionId),
                    modelVars.GetFrequencyG(regionId),
                    modelVars.GetFrequencyT(regionId),
                    modelVars.GetTTRatio(regionId),
                    modelVars.GetCalcFreqsFromData(regionId),
                    modelVars.GetPerBaseErrorRate(regionId)
                    );
            return model;
        }
        break;
        case Brownian:
        {
            BrownianModel * model
                = new BrownianModel(
                    locus.GetNmarkers(),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId)
                    );
            return model;
        }
        break;
        case Stepwise:
        {
            StepwiseModel * model
                = new StepwiseModel(
                    locus.GetNmarkers(),
                    dataPackPlus.GetUniqueAlleles(regionId.GetRegion(),
                                                  regionId.GetLocus()),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId)
                    );
            return model;
        }
        break;
        case KAllele:
        {
            //If we have trait data with unknown haplotypes, we might have
            // some possible alleles that are not represented in the tip data.
            // In this case, we need to get those strings out of the individuals.
            KAlleleModel * model
                = new KAlleleModel(
                    locus.GetNmarkers(),
                    dataPackPlus.GetUniqueAlleles(regionId.GetRegion(),
                                                  regionId.GetLocus()),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId)
                    );
            return model;
        }
        break;
        case GTR:
        {
            GTRModel * model
                = new GTRModel(
                    locus.GetNmarkers(),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId),
                    modelVars.GetFrequencyA(regionId),
                    modelVars.GetFrequencyC(regionId),
                    modelVars.GetFrequencyG(regionId),
                    modelVars.GetFrequencyT(regionId),
                    modelVars.GetGTR_AC(regionId),
                    modelVars.GetGTR_AG(regionId),
                    modelVars.GetGTR_AT(regionId),
                    modelVars.GetGTR_CG(regionId),
                    modelVars.GetGTR_CT(regionId),
                    modelVars.GetGTR_GT(regionId),
                    modelVars.GetPerBaseErrorRate(regionId)
                    );
            return model;
        }
        break;
        case MixedKS:
        {
            MixedKSModel * model
                = new MixedKSModel(
                    locus.GetNmarkers(),
                    dataPackPlus.GetUniqueAlleles(regionId.GetRegion(),
                                                  regionId.GetLocus()),
                    modelVars.GetNumCategories(regionId),
                    modelVars.GetCategoryRates(regionId),
                    modelVars.GetCategoryProbabilities(regionId),
                    modelVars.GetAutoCorrelation(regionId),
                    modelVars.GetNormalization(regionId),
                    modelVars.GetRelativeMuRate(regionId),
                    modelVars.GetAlpha(regionId),
                    modelVars.GetOptimizeAlpha(regionId)
                    );
            return model;
        }
        break;
    }
    throw implementation_error("Registry::CreateDataModel needs to know how to build a "
                               + ToString(modelType) + " model ");
}

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

void Registry::InstallDataModels(UIInterface& ui)
{
    UIVars & vars = ui.GetCurrentVars();

    // install models for each region
    for(long regionId = 0; regionId < vars.datapackplus.GetNumRegions() ; regionId++)
    {
        Region & thisRegion = datapack.GetRegion(regionId);

        for(long locusId = 0; locusId < thisRegion.GetNloci(); locusId++)
        {
            Locus & thisLocus = thisRegion.GetLocus(locusId);
            UIRegId regId(regionId, locusId, vars);

            DataModel * modelForThisLocus
                = CreateDataModel(vars.datamodel, thisLocus, vars.datapackplus, regId);

            // whatever model we've created, attach it to this locus
            thisLocus.SetDataModelOnce(DataModel_ptr(modelForThisLocus));

            // let's make sure we've done this right
            string errString;
            if(!thisLocus.IsValidLocus(errString))
            {
                throw implementation_error("Inconsistent region "+errString);
            }
        }
    }
}

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

void Registry::InstallForcesAllOverThePlace(UIInterface& ui)
{

    UIVars& vars = ui.GetCurrentVars(); //Not const because of FixGroups().

    // get list of forces to create and install into the forcesummary
    ForceTypeVec1d activeForceTypes = vars.forces.GetPhase2ActiveForces();
    LongVec1d activeForceSizes = vars.forces.GetForceSizes();
    ForceTypeVec1d::iterator iter;
    LongVec1d::iterator forceSize_iter;
    // MFIX -- need to get the multipliers from something....
    const DoubleVec1d multipliers;

    if (vars.forces.GetForceOnOff(force_REGION_GAMMA))
    {
        vector<Parameter> emptyParamVector;
        RegionGammaForce *pRegionGammaForce =
            new RegionGammaForce(emptyParamVector,
                                 vars.forces.GetIdentGroups(force_REGION_GAMMA),
                                 vars.forces.GetMultGroups(force_REGION_GAMMA),
                                 multipliers,
                                 vars.forces.GetDefaultPrior(force_REGION_GAMMA));
        RegionGammaInfo *pRegionGammaInfo =
            new RegionGammaInfo(vars.forces.GetStartValue(force_REGION_GAMMA, 0),
                                vars.forces.GetParamstatus(force_REGION_GAMMA, 0).Status(),
                                vars.forces.GetDoProfile(force_REGION_GAMMA, 0),
                                vars.forces.GetProfileType(force_REGION_GAMMA),
                                vars.forces.GetProfileTypeSummaryDescription(
                                    force_REGION_GAMMA),
                                pRegionGammaForce);
        Register(pRegionGammaInfo);

        // Prevent this force from being added to forceparams.
        for (iter = activeForceTypes.begin(), forceSize_iter = activeForceSizes.begin();
             iter != activeForceTypes.end() && *iter != force_REGION_GAMMA;
             iter++, forceSize_iter++)
        {
            // purposely empty loop body
        }

        if (activeForceTypes.end() == iter || activeForceSizes.end() == forceSize_iter)
        {
            string msg = "Registry::InstallForcesAllOverThePlace(), ";
            msg += "vars.forces.GetForceOnOff(";
            msg += "force_REGION_GAMMA) claimed this force was ";
            msg += "active, but vars.forces.";
            msg += "GetActiveForces() did not return the label ";
            msg += "for this force.";
            throw implementation_error(msg);
        }
        activeForceTypes.erase(iter);
        activeForceSizes.erase(forceSize_iter);
    }

    ForceParameters forceparams(global_region, activeForceTypes, activeForceSizes);

    unsigned long paramvecIndex = 0;
    ForceVec allforces;
    bool logisticSelectionIsOn(vars.forces.GetForceOnOff(force_LOGISTICSELECTION));
    CoalesceLogisticSelectionPL *pCoalLogisticSelectionPL(NULL);
    DiseaseLogisticSelectionPL *pDiseaseLogisticSelectionPL(NULL);

    for(iter = activeForceTypes.begin(); iter != activeForceTypes.end(); iter++)
    {
        const force_type thisForceType = *iter;
        vars.forces.FixGroups(thisForceType);
        if (!vars.forces.GetForceZeroesValidity(thisForceType))
        {
            string err = "Invalid settings for force ";
            err += ToString(thisForceType) + ".  Too many parameters are set ";
            err += "invalid or have a start value of 0.0.";
            throw data_error(err);
        }
        // build parameters for this force
        long nParamsForThisForce = vars.forces.GetNumParameters(thisForceType);
        vector<Parameter> parameters;
        for(long paramIndex = 0; paramIndex < nParamsForThisForce;
            paramIndex++, paramvecIndex++)
        {
            bool thisParamValid = vars.forces.GetParamValid(thisForceType,paramIndex);
            ParamStatus mystatus=vars.forces.GetParamstatus(thisForceType,paramIndex);
#if 0 // JDEBUG -- assumes stochastic selection is in play, used for
      // early testing, remove once UI is handled correctly
            if (thisForceType == force_COAL)
            {
                if (paramIndex == 0) pstat=pstat_multiplicative_head;
                else pstat = pstat_multiplicative;
            }
#endif
            proftype ptype=vars.forces.GetProfileType(thisForceType,paramIndex);
            if(thisParamValid)
            {
                force_type phase2type(vars.forces.GetPhase2Type(thisForceType));
                parameters.push_back
                    (Parameter
                     (mystatus,
                      paramvecIndex,
                      vars.datapackplus.GetParamName(phase2type,paramIndex,false),
                      vars.datapackplus.GetParamName(phase2type,paramIndex,true),
                      phase2type,
                      vars.forces.GetStartMethod(thisForceType,paramIndex),
                      ptype,
                      vars.forces.GetPrior(thisForceType,paramIndex),
                      vars.forces.GetTrueValue(thisForceType,paramIndex)
                         ));
            }
            else
            {
                parameters.push_back(Parameter(ParamStatus(pstat_invalid), paramvecIndex));
                assert (!mystatus.Valid()); //Warn the user about their input? -LS
            }
        }
        Force * newForce = NULL;
        long maxEvents = vars.forces.GetMaxEvents(thisForceType);

        // JDEBUG--this is wrong! need to get UI multiplicative interface....
        const vector<ParamGroup> multgroups = vars.forces.
            GetMultGroups(thisForceType);
        // JDEBUG--this is wrong! need to get UI multiplicative interface....
        const DoubleVec1d paramgroupmultipliers;

#if 0 // JDEBUG -- assumes stochastic selection is in play, used for
      // early testing, remove once UI is handled correctly
        vector<ParamGroup> multgroups = vars.forces.GetMultGroups(thisForceType);
        DoubleVec1d paramgroupmultipliers;
        if (thisForceType == force_COAL)
        {
            multgroups.clear();
            LongVec1d multindices;
            multindices.push_back(0);
            multindices.push_back(1);
            ParamGroup mults = make_pair(pstat_multiplicative,multindices);
            multgroups.push_back(mults);
            paramgroupmultipliers.push_back(99.0);
        }
#endif

        const vector<ParamGroup> identgroups =
            vars.forces.GetIdentGroups(thisForceType);

        for (unsigned long gnum=0; gnum<identgroups.size(); gnum++)
        {
            if (identgroups[gnum].first.Status() == (pstat_identical) ||
                identgroups[gnum].first.Status() == pstat_multiplicative)
            {
                //We need to re-name one parameter for every such group.
                long pindex = identgroups[gnum].second[0];
                assert(parameters[pindex].GetStatus().Status() == pstat_identical_head ||
                       parameters[pindex].GetStatus().Status() == pstat_multiplicative_head);
                // MFIX probably need to handle multiplicatives here
                parameters[pindex].SetShortName(vars.GetParamNameWithConstraint(thisForceType, pindex, false));
                parameters[pindex].SetName(vars.GetParamNameWithConstraint(thisForceType, pindex, true));
            }
        }

        switch(thisForceType)
        {
            case force_COAL:
                newForce = new CoalForce(
                    parameters,
                    maxEvents,
                    vars.forces.GetForceOnOff(force_GROW),
                    logisticSelectionIsOn,
                    identgroups,
                    multgroups,
                    paramgroupmultipliers,
                    vars.forces.GetDefaultPrior(thisForceType));
                if (logisticSelectionIsOn)
                {
                    pCoalLogisticSelectionPL =
                        dynamic_cast<CoalesceLogisticSelectionPL*>
                        (newForce->GetPLForceFunction());
                    if (!pCoalLogisticSelectionPL)
                    {
                        string msg = "Registry::InstallForcesAllOverThePlace(), ";
                        msg += "detected that ";
                        msg += "the logistic selection force is on, but failed to ";
                        msg += "obtain a ";
                        msg += "CoalesceLogisticSelectionPL object from a ";
                        msg += "CoalForce object.";
                        throw implementation_error(msg);
                    }
                }
                break;

            case force_MIG:
                //cerr << "create force_MIG" << endl;
                newForce = new MigForce
                    (
                        parameters,
                        maxEvents,
                        vars.datapackplus.GetNPartitionsByForceType(thisForceType),
                        identgroups,
                        multgroups,
                        paramgroupmultipliers,
                        vars.forces.GetDefaultPrior(thisForceType));
                break;

            case force_DIVMIG:
                //cerr << "create force_DIVMIG" << endl;
                newForce = new DivMigForce
                    (
                        parameters,
                        maxEvents,
                        vars.datapackplus.GetNPartitionsByForceType(thisForceType),
                        identgroups,
                        multgroups,
                        paramgroupmultipliers,
                        vars.forces.GetDefaultPrior(thisForceType));
                break;

            case force_DISEASE:
                newForce = new DiseaseForce(
                    parameters,
                    maxEvents,
                    logisticSelectionIsOn,
                    vars.datapackplus.GetNPartitionsByForceType(thisForceType),
                    vars.forces.GetDiseaseLocation(),
                    identgroups,
                    multgroups,
                    paramgroupmultipliers,
                    vars.forces.GetDefaultPrior(thisForceType));
                if (logisticSelectionIsOn)
                {
                    pDiseaseLogisticSelectionPL =
                        dynamic_cast<DiseaseLogisticSelectionPL*>
                        (newForce->GetPLForceFunction());
                    if (!pDiseaseLogisticSelectionPL)
                    {
                        string msg = "Registry::InstallForcesAllOverThePlace(), ";
                        msg += "detected that ";
                        msg += "the logistic selection force is on, but ";
                        msg += "failed to obtain a ";
                        msg += "DiseaseLogisticSelectionPL object from a ";
                        msg += "DiseaseForce object.";
                        throw implementation_error(msg);
                    }
                }
                break;

            case force_REC:
                newForce = new RecForce(
                    parameters,
                    maxEvents,
                    identgroups,
                    multgroups,
                    paramgroupmultipliers,
                    vars.forces.GetDefaultPrior(thisForceType));
                break;

            case force_DIVERGENCE:
            {
                newForce = new DivForce(
                    parameters,
                    maxEvents,
                    identgroups,
                    multgroups,
                    paramgroupmultipliers,
                    vars.forces.GetDefaultPrior(thisForceType),
                    vars.forces.GetNewPops(),
                    vars.forces.GetAncestors(),
                    registry.GetDataPack()); // const access, in order to call GetPartitionNumber
                break;
            }

            case force_EXPGROWSTICK:
            case force_GROW:
                if (vars.forces.GetGrowthType() == growth_STICKEXP)
                {
                    // stick exponential model
                    newForce = new StickExpGrowForce(
                        parameters,
                        maxEvents,
                        identgroups,
                        multgroups,
                        paramgroupmultipliers,
                        vars.forces.GetDefaultPrior(thisForceType));
                }
                else                    // default growth model
                {
                    newForce = new GrowthForce(
                        parameters,
                        maxEvents,
                        identgroups,
                        multgroups,
                        paramgroupmultipliers,
                        vars.forces.GetDefaultPrior(thisForceType));
                }
                break;

            case force_LOGISTICSELECTION:
                newForce = new LogisticSelectionForce(
                    parameters,
                    maxEvents,
                    paramvecIndex - 1, // -1 to counteract "for" loop
                    identgroups,
                    multgroups,
                    paramgroupmultipliers,
                    vars.forces.GetDefaultPrior(thisForceType));

                // temporary HACK:  Assumes force_COAL processed before force_LOGISTICSELECTION.
                if (!pCoalLogisticSelectionPL)
                {
                    string msg = "Registry::InstallForcesAllOverThePlace(), ";
                    msg += "while processing ";
                    msg += "the logistic selection force, expected to ";
                    msg += "have access to a ";
                    msg += "CoalesceLogisticSelectionPL object, but ";
                    msg += "this object was not found.";
                    throw implementation_error(msg);
                }
                pCoalLogisticSelectionPL->
                    SetSelectionCoefficientLocation(paramvecIndex - 1);
                pDiseaseLogisticSelectionPL->
                    SetSelectionCoefficientLocation(paramvecIndex - 1);
                // -1 because paramvecIndex gets incremented one extra time
                // when the "for" loop stops
                break;

            case force_LOGSELECTSTICK:
                newForce = new StickSelectForce(parameters,maxEvents,
                                                identgroups,multgroups,paramgroupmultipliers,
                                                vars.forces.GetDefaultPrior(thisForceType));
                // NB: unlike other forces, the StickSelectForce does not create
                // a PLForces object on creation.  Instead, one will be created
                // when the PLForces are installed into the PostLike objects (we
                // need special case code, like ModifyEvent(), to deal with Stick
                // Selection anyway, so we consolidate it all there....
                break;

            case force_REGION_GAMMA:
            {
                string msg = "Registry::InstallForcesAllOverThePlace() is using ";
                msg += "force_REGION_GAMMA in two conflicting ways.";
                throw implementation_error(msg);
                break;
            }

            case force_NONE:
            {
                string msg = "Registry::InstallForcesAllOverThePlace() is trying to make ";
                msg += "a NONE force.";
                throw implementation_error(msg);
                break;
            }
        }

        assert(newForce != NULL);
        allforces.push_back(newForce);
        forceparams.SetGlobalParametersByTag(thisForceType, vars.forces.GetStartValues(thisForceType));
    }

#if 0    // JTEMP create a DivergenceForce and a DivMigForce
    // build parameters for Divergence force
    long nParamsForThisForce = 9L:
        vector<Parameter> parameters;
    for(long paramIndex = 0; paramIndex < nParamsForThisForce;
        paramIndex++, paramvecIndex++)
    {
        bool thisParamValid = false;
        paramstatus pstat=pstat_constant;
        proftype ptype=profile_NONE;
        parameters.push_back(Parameter(pstat_invalid, paramvecIndex));
    }
    Force * newForce = NULL;
    long maxEvents = 1000L;

    vector<Epoch> epochs;
    vector<long> here, departing;
    here.push_back(0);
    here.push_back(1);
    Epoch firstepoch(here,departing,0L);
    epochs.push_back(firstepoch);
    departing = here;
    here.clear();
    here.push_back(2);
    Epoch secondepoch(here,departing,2L);

    newForce = new DivForce(
        parameters,
        maxEvents,
        identgroups,
        multgroups,
        paramgroupmultipliers,
        vars.forces.GetDefaultPrior(thisForceType),
        epochs);

#if 0
    if(thisParamValid)
    {
        force_type phase2type(vars.forces.GetPhase2Type(thisForceType));
        parameters.push_back
            (Parameter
             (pstat,
              paramvecIndex,
              vars.datapackplus.GetParamName(phase2type,paramIndex,false),
              vars.datapackplus.GetParamName(phase2type,paramIndex,true),
              phase2type,
              vars.forces.GetStartMethod(thisForceType,paramIndex),
              ptype,
              vars.forces.GetPrior(thisForceType,paramIndex),
              vars.forces.GetTrueValue(thisForceType,paramIndex)
                 ));
    }
    else
    {
        parameters.push_back(Parameter(pstat_invalid, paramvecIndex));
        assert (pstat==pstat_invalid); //Warn the user about their input? -LS
    }
#endif

#endif // end JTEMP

    // install forces into the forcesummary.
    forcesummary = new ForceSummary(allforces, forceparams, GetDataPack());
    assert (forcesummary->IsValidForceSummary());

    // Create and register a ProtoTree using the new forcesummary object.
    Register(forcesummary->CreateProtoTreeSummary());
}

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

#ifdef LAMARC_QA_SINGLE_DENOVOS

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

void Registry::AddDenovoMaxRejectCount(long addMe)
{
    m_denovoMaxRejectCount += addMe;
}

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

long Registry::GetDenovoMaxRejectCount()
{
    return m_denovoMaxRejectCount;
}

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

void Registry::AddDenovoTreeRejectCount(long addMe)
{
    m_denovoTreeRejectCount += addMe;
}

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

long Registry::GetDenovoTreeRejectCount()
{
    return m_denovoTreeRejectCount;
}

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

long Registry::GetDenovoCount()
{
    return m_denovoCount;
}

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

void Registry::SetDenovoCount(long count)
{
    m_denovoCount = count;
}

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

#endif // LAMARC_QA_SINGLE_DENOVOS

//____________________________________________________________________________________
