// $Id: timemanager.cpp,v 1.17 2018/01/03 21:32:58 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>       // for debug PrintStick function
#include <iterator>       // for ostream_iterator in debug Print fns
#include <limits>         // for epsilon and numeric_limits

#include "timemanager.h"
#include "forceparam.h"   // parameter handling
#include "random.h"       // random choice of times
#include "constants.h"    // DBL_MAX
#include "definitions.h"  // typedefs

#include "registry.h"     // to get ForceSummary
#include "force.h"        // Force-specific partition handling
#include "mathx.h"        // for SafeProductWithExp
#include "treesum.h"      // for ScoreStick!

//------------------------------------------------------------------------------------
// TimeManager

TimeManager::TimeManager()
    : randomSource(registry.GetRandom())
{
    // deliberately blank
} // TimeManager default ctor

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

TimeManager::TimeManager(const TimeManager& src)
    : randomSource(src.randomSource)
{
    // deliberately blank
} // TimeManager copy ctor

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

TimeManager& TimeManager::operator=(const TimeManager&)
{
    // it is not necessary to copy randomSource, as it should
    // already be correct
    return *this;
} // TimeManager operator=

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

void TimeManager::MakeStickUsingBranches(const ForceParameters&,
                                         const std::vector<std::pair<double,LongVec1d> >&)
{
    // we do nothing since there is no stick
} // TimeManager::MakeStickUsingBranches

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

void TimeManager::MakeStickTilTime(const ForceParameters&, double)
{
    // we do nothing since there is no stick
} // TimeManager::MakeStickTilTime

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

bool TimeManager::UsingStick() const
{
    return false;
} // TimeManager::UsingStick

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

void TimeManager::CopyStick(const TimeManager&)
{
    // we do nothing since there is no stick
} // TimeManager::UsingStick

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

void TimeManager::ClearStick()
{
    // we do nothing since there is no stick
} // TimeManager::ClearStick

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

void TimeManager::SetStickParameters(const ForceParameters&)
{
    // we do nothing since there is no stick
} // TimeManager::SetStickParameters

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

void TimeManager::ScoreStick(TreeSummary&) const
{
    // we do nothing since there is no stick
} // TimeManager::ScoreStick

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

void TimeManager::ChopOffStickAt(double)
{
    // we do nothing since there is no stick
} // TimeManager::ChopOffStickAt

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickThetasToFile(std::ofstream&) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickThetasToFile

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickFreqsToFile(std::ofstream&) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickFreqsToFile

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickFreqsToFileAtTime(std::ofstream&, double) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickFreqsToFileAtTime

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickThetasToFileForJoint300(std::ofstream&) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickThetasToFileForJoint300

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickToFile(std::ofstream&) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickToFile

//------------------------------------------------------------------------------------
// Debugging function.

void TimeManager::PrintStickFreqsToFileForJoint300(std::ofstream&) const
{
    // we do nothing since there is no stick
} // TimeManager::PrintStickFreqsToFileForJoint300

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// ConstantTimeManager

DoubleVec1d ConstantTimeManager::XpartThetasAtT(double, const ForceParameters& fp)
{
    return fp.GetRegionalThetas();
} // XpartThetasAtT

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

DoubleVec1d ConstantTimeManager::PartitionThetasAtT(double, force_type force, const ForceParameters& fp)
{
    DoubleVec1d thetas(fp.GetRegionalThetas());
    return dynamic_cast<PartitionForce*>(*registry.GetForceSummary().GetForceByTag(force))->SumXPartsToParts(thetas);
} // PartitionThetasAtT

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

double ConstantTimeManager::TimeOfActiveCoal(double tstart,
                                             const LongVec1d& lineages, const ForceParameters& fp, long& targetxpart,
                                             double)
{
    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart

    // Note:  The expectation value of this "delta t" is theta/(k*(k-1)),
    // where k (the number of active lineages) and theta correspond to the
    // population which yields the smallest "delta t."

    double newtime;
    long i;
    DoubleVec1d thetas = fp.GetRegionalThetas();
    long nxparts = thetas.size();
    double besttime = DBL_MAX;
    long target = 0;

    for (i = 0; i < nxparts; ++i)
    {
        // If there are at least two active lineages in this population ...
        if (lineages[i] > 1)
        {
            double randnum = randomSource.Float();

            // Compute the time of the next coalescence.
            newtime = - log(randnum) / (lineages[i] * (lineages[i] - 1.0) / thetas[i]);

            // Keep it only if it's shorter than previous ones.
            if (newtime < besttime)
            {
                besttime = newtime;
                target = i;
            }
        }
    }

    if (besttime == DBL_MAX)
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }
    else
    {
        assert (besttime >= 0.0);
        targetxpart = target;
        return tstart + besttime;
    }
} // TimeOfActiveCoal

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

double ConstantTimeManager::TimeOfInactiveCoal(double tstart,
                                               const LongVec1d& activelines, const LongVec1d& inactivelines,
                                               const ForceParameters& fp, long& targetxpart, double)
{
    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart

    // Note:  The expectation value of this "delta t" is theta/(j*k),
    // where j and k (the numbers of inactive and active lineages) and theta
    // correspond to the population which yields the smallest "delta t."

    double newtime = 0.0;
    long i;
    DoubleVec1d thetas = fp.GetRegionalThetas();
    long nxparts = thetas.size();
    double besttime = DBL_MAX;
    long target = 0;

    for (i = 0; i < nxparts; ++i)
    {
        if (activelines[i] > 0 && inactivelines[i] > 0)
        {
            // denominator contains j*k, inactives*actives,
            // instead of k*(k-1)
            newtime = - log (randomSource.Float()) /
                (2.0 * activelines[i] * inactivelines[i] / thetas[i]);
            if (newtime < besttime)
            {
                besttime = newtime;
                target = i;
            }
        }
    }

    if (besttime == DBL_MAX)
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }
    else
    {
        assert (besttime >= 0.0);
        targetxpart = target;
        return tstart + besttime;
    }

} // TimeOfInactiveCoal

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

double ConstantTimeManager::TimeOfTraitMutation(double tstart,
                                                const LongVec1d& lineages, const ForceParameters& fp, long& tiptrait,
                                                long& roottrait, double)
{
    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart

    // MDEBUG NOT CORRECT in presence of migration due to use of
    // crosspartition rather than partition Thetas
    // needs a function PartitionForce::ComputePartitionTheta

    double newtime = 0.0;
    DoubleVec1d thetas = fp.GetRegionalThetas();
    // mus[0] is a->A, mus[1] is A->a in forward time
    DoubleVec1d mus = fp.GetOnlyDiseaseRates();
    double besttime = DBL_MAX;
    long tiptarget, roottarget;

    // unrolling the loop since we only have 2 partitions and
    // avoid partition/xpartition mapping by doing so....
    if (lineages[0] > 0)
    {
        newtime = - log (randomSource.Float()) /
            (lineages[0] * mus[0] * thetas[1]/thetas[0]);
        if (newtime < besttime)
        {
            besttime = newtime;
            tiptarget = 0;
            roottarget = 1;
        }
    }

    if (lineages[1] > 0)
    {
        newtime = - log (randomSource.Float()) /
            (lineages[1] * mus[1] * thetas[0]/thetas[1]);
        if (newtime < besttime)
        {
            besttime = newtime;
            tiptarget = 1;
            roottarget = 0;
        }
    }

    if (besttime == DBL_MAX)
    {
        // no event is possible
        tiptrait = FLAGLONG;
        roottrait = FLAGLONG;
        return FLAGDOUBLE;
    }
    else
    {
        assert (besttime >= 0.0);
        tiptrait = tiptarget;
        roottrait = roottarget;
        return tstart + besttime;
    }


} // TimeOfTraitMutation

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// ExpGrowTimeManager

DoubleVec1d ExpGrowTimeManager::XpartThetasAtT(double t, const ForceParameters& fp)
{
    DoubleVec1d thetas(fp.GetRegionalThetas()),growths(fp.GetGrowthRates());
    assert(thetas.size() == growths.size());
    DoubleVec1d newthetas(thetas.size());
    unsigned long param;
    for(param = 0; param < thetas.size(); ++param)
    {
        newthetas[param] = SafeProductWithExp(thetas[param],-growths[param]*t);
    }
    return newthetas;
} // XpartThetasAtT

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

DoubleVec1d ExpGrowTimeManager::PartitionThetasAtT(double t, force_type force, const ForceParameters& fp)
{
    DoubleVec1d thetas(fp.GetRegionalThetas()),growths(fp.GetGrowthRates());
    return dynamic_cast<PartitionForce*>(*registry.GetForceSummary().
                                         GetForceByTag(force))->SumXPartsToParts(thetas,growths,t);
} // PartitionThetasAtT

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

double ExpGrowTimeManager::TimeOfActiveCoal(double tstart,
                                            const LongVec1d& lineages, const ForceParameters& fp, long& targetxpart,
                                            double)
{
    // Computes the timestamp of the end of the interval by means of computing "delta t" and adding it to tstart.

    // Note:  The expectation value of this "delta t" is:
    //                           (1/g)*ExpE1(k*(k-1)*exp(g*tstart)/(g*theta)),
    // where g, theta, and k are the growth rate, theta, and number of active lineages
    // for the population which yields the smallest "delta t."
    // For details on the function ExpE1(), see mathx.cpp.

    double newtime = 0.0;
    double magicstart;
    long i;
    DoubleVec1d thetas = fp.GetRegionalThetas();
    DoubleVec1d growths = fp.GetGrowthRates();
    long nxparts = thetas.size();
    double besttime = DBL_MAX;
    long target = 0;

    // compute coalescence terms per cross partition
    for (i = 0; i < nxparts; ++i)
    {
        // if there are at least two active lineages in this population...
        if (lineages[i] > 1)
        {
            // compute the time of the next coalescence
            // first do computation in "magic time"
            newtime =  - log(randomSource.Float()) /
                (lineages[i] * (lineages[i] - 1.0) / thetas[i]);

            // now convert to "real time"
            if (growths[i] != 0)
            {
                // convert tstart to magical time
                magicstart = -(1.0 - exp(growths[i] * tstart)) / growths[i];
                // add tstart to displacement
                newtime += magicstart;
                // comvert result to real time
                if (1.0 + growths[i] * newtime > 0.0)
                {
                    newtime = log(1.0 + growths[i] * newtime)/growths[i];
                    // convert back to displacement
                    newtime -= tstart;
                    // guard against underflow
                    if (newtime < DBL_EPSILON)
                    {
                        tinypopulation_error e("bad time proposed in Event::PickTime");
                        throw e;
                    }
                }
                else
                {
                    newtime = DBL_BIG; // make this interval length very unlikely to win the race
                }
            }
            // (else newtime holds the same number that would have been
            // computed by ActiveCoal, in the absence of growth)

            // is this the best time so far?
            if (newtime < besttime)
            {
                besttime = newtime;
                target = i;
            }
        }
    }

    if (besttime == DBL_MAX)
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }
    else
    {
        assert (besttime >= 0.0);
        targetxpart = target;
        return tstart + besttime;
    }

} // TimeOfActiveCoal

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

double ExpGrowTimeManager::TimeOfInactiveCoal(double tstart,
                                              const LongVec1d& activelines, const LongVec1d& inactivelines,
                                              const ForceParameters& fp, long& targetxpart, double)
{
    // Computes the timestamp of the end of the interval by means of computing "delta t" and adding it to tstart.

    // Note:  The expectation value of this "delta t" is:
    //                           (1/g)*ExpE1(j*k*exp(g*tstart)/(2*g*theta)),
    // where g, theta, j, and k are the growth rate, theta, and numbers of
    // inactive and active lineages for the population which yields the
    // smallest "delta t."  (Note also the factor of 2, absent from ActiveGrowCoal.)
    // For details on the function ExpE1(), see mathx.cpp.

    double newtime = 0.0;
    double magicstart;
    long i;
    DoubleVec1d thetas = fp.GetRegionalThetas();
    DoubleVec1d growths = fp.GetGrowthRates();
    long nxparts = thetas.size();
    double besttime = DBL_MAX;
    long target = 0;

    for (i = 0; i < nxparts; ++i)
    {
        if (activelines[i] > 0 && inactivelines[i] > 0)
        {
            // first do calculation in "magic time"
            // Numerator contains j*k, inactives*actives,
            // instead of k*(k-1)
            // Also, denominator contains a factor of 2, unlike ActiveGrowCoal.
            newtime = - log(randomSource.Float()) /
                ((2.0 / thetas[i]) * activelines[i] * inactivelines[i]);

            // then convert to "real time"
            if (growths[i] != 0)
            {
                // convert tstart to magical time
                magicstart = -(1.0 - exp(growths[i] * tstart)) / growths[i];
                // add tstart to displacement
                newtime += magicstart;
                if (1.0 + growths[i] * newtime > 0.0)
                {
                    // convert result to real time
                    newtime = log(1.0 + growths[i] * newtime)/growths[i];
                    // convert back to displacement
                    newtime -= tstart;
                    // guard against underflow
                    if (newtime < DBL_EPSILON)
                    {
                        tinypopulation_error e("bad time proposed in Event::PickTime");
                        throw e;
                    }
                }
                else
                    newtime = DBL_BIG; // make this interval length
                // unlikely to win the race
            }

            // is this the best time so far?
            if (newtime < besttime)
            {
                besttime = newtime;
                target = i;
            }
        }
    }

    if (besttime == DBL_MAX)
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }
    else
    {
        assert (besttime >= 0.0);
        targetxpart = target;
        return tstart + besttime;
    }
} // TimeOfInactiveCoal

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

double ExpGrowTimeManager::TimeOfTraitMutation(double,
                                               const LongVec1d&, const ForceParameters&, long&,
                                               long&, double)
{
    // Computes a displacement, a.k.a. "delta t" -- the length of the time
    // interval, not the timestamp for the end of the interval.
    assert(false);  // Can't combine Disease and ExpGrowth right now!
    return 0.0;     // Silence compiler warning.
} // TimeOfTraitMutation

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// LogSelectTimeManager

DoubleVec1d LogSelectTimeManager::XpartThetasAtT(double t, const ForceParameters& fp)
{
    DoubleVec1d thetas(fp.GetRegionalThetas()),
        selcoeffs(fp.GetLogisticSelectionCoefficient());

    if (2 != thetas.size())
    {
        string msg = "LogisticSelectionTimeSize::SizeAt(), needed to receive ";
        msg += "two thetas, one for each allele, but instead received ";
        msg += ToString(thetas.size());
        msg + " thetas.";
        throw implementation_error(msg);
    }

    if (1 != selcoeffs.size())
    {
        string msg = "LogisticSelectionTimeSize::SizeAt(), needed to receive ";
        msg += "1 selection coefficient, but instead received ";
        msg += ToString(selcoeffs.size());
        msg + " coefficients.";
        throw implementation_error(msg);
    }

    DoubleVec1d newthetas(2);
    double t_term = SafeProductWithExp(thetas[1], selcoeffs[0]*t);
    newthetas[0] = (thetas[0]+thetas[1])/(thetas[0] + t_term);
    newthetas[1] = newthetas[0] * t_term;
    newthetas[0] = newthetas[0] * thetas[0];

    return newthetas;
} // XpartThetasAtT

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

DoubleVec1d LogSelectTimeManager::PartitionThetasAtT(double, force_type, const ForceParameters&)
{
    // MDEBUG not done!
    assert(false); // MDEBUG we are not ready for this yet
    DoubleVec1d answers;
    return answers;

} // PartitionThetasAtT

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

double LogSelectTimeManager::TimeOfActiveCoal(double tstart,
                                              const LongVec1d& lineages, const ForceParameters& fp, long& targetxpart,
                                              double)
{
    DoubleVec1d thetas = fp.GetRegionalThetas();
    double selectionCoefficient = fp.GetLogisticSelectionCoefficient()[0];
    long nxparts = thetas.size();

    if (2 != nxparts)
    {
        string msg = "ActiveLogisticSelectionCoal::PickTime() called with nxparts = ";
        msg += ToString(nxparts);
        msg += ".  nxparts must equal 2, reflecting one population with the major ";
        msg += "allele and one population with the minor allele.";
        throw implementation_error(msg);
    }

    if (0.0 == thetas[0] || 0.0 == thetas[1])
    {
        string msg = "ActiveLogisticSelectionCoal::PickTime() received an invalid Theta ";
        msg += "of 0.0.";
        throw impossible_error(msg);
    }

    if (lineages[0] <= 1 && lineages[1] <= 1)
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }

    // First, compute the interval lengths in "magic time"--drawn from an
    // exponential distribution assuming constant size for population "A"
    // and for population "a".
    double dtau_A(FLAGDOUBLE), dtau_a(FLAGDOUBLE); // important:  these are < 0

    if (lineages[0] > 1)
        dtau_A = -log(randomSource.Float()) /
            (lineages[0]*(lineages[0] - 1.0)/thetas[0]);

    if (lineages[1] > 1)
        dtau_a = -log(randomSource.Float()) /
            (lineages[1]*(lineages[1] - 1.0)/thetas[1]);

    return tstart + LogisticSelectionPickTime(thetas[0],thetas[1],selectionCoefficient,
                                              tstart,dtau_A,dtau_a,&targetxpart);

} // TimeOfActiveCoal

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

// This helper function performs the transformations from "magic time"
// into "real time."
// These transformations are:
//
// dtau_A = (theta_a0*exp(s*t_s)*((exp(s*dt_A)-1)/s) +
//           theta_A0*dt_A)/(theta_A0+theta_a0)
//
// dtau_a = (theta_A0*exp(-s*t_s)*((exp(-s*dt_a)-1)/(-s)) +
//           theta_a0*dt_a)/(theta_A0+theta_a0)
//
// Given dtau_A and dtau_a, we solve for dt_A and dt_a, and return the
// shorter of these, along with the identity of the population which "won
// the horse race."
// Note that each transformation includes dt in both an exponential term
// and a linear term.
// In some cases, one of these terms is negligible; these are explained in
// the code below.
// When neither the exponential nor the linear term is negligible, the
// transformation can only be solved by iterative guessing.
//
// Note:  If this function is called, then a coalescence is possible
// in at least one of the two populations.  This is a requirement.
//
double LogSelectTimeManager::LogisticSelectionPickTime(const double& theta_A0, const double& theta_a0,
                                                       const double& s, const double& t_s,
                                                       const double& dtau_A, const double& dtau_a,
                                                       long* pChosenPopulation)
{
    double dt_A(FLAGDOUBLE), dt_a(FLAGDOUBLE); // important: these are < 0
    double minimum_possible_dt(t_s * std::numeric_limits<double>::epsilon());
    double ln_theta_A0(log(theta_A0)), ln_theta_a0(log(theta_a0)), a, b(0.0), c, x;

    if (s > 0)
    {
        if (s*t_s > EXPMAX - ln_theta_a0)
        {
            // theta_a0 * exp(s*t_s) overflows.
            // This means the expectation E[dt_A] underflows to zero,
            // so if we return any nonzero dt_A, no matter how short,
            // it will be many times longer than its expected value.
            // Return the shortest interval possible (guaranteed to win the
            // "horse race"), if a coalescence is allowed in population "A"
            // (not allowed if k = 1).
            if (dtau_A > .999*FLAGDOUBLE)
            {
                *pChosenPopulation = 0;
                return minimum_possible_dt;
            }
            // Otherwise, population "a" coalesces.  Underflow simplifies the
            // result.
            *pChosenPopulation = 1;
            return dtau_a*(theta_A0+theta_a0)/theta_a0;
        }

        if (s*t_s > 4.0*LOG10 + ln_theta_A0 - ln_theta_a0)
        {
            if (dtau_A > .999*FLAGDOUBLE)
            {
                // It's possible to coalesce in population "A",
                // and the linear term in the right-hand side of the
                // transformation for dtau_A may safely be ignored.
                b = SafeProductWithExp(theta_a0,s*t_s); // guaranteed > 0
                c = -dtau_A*(theta_A0+theta_a0); // c < 0
                x = s*(-c)/b;
                if (x > 1.0e-04)
                    dt_A = (1.0/s)*log(1.0 + x);
                else
                    dt_A = x/s; // because log(1+x) = x + O(x^2)
                if (dt_A < minimum_possible_dt)
                    dt_A = minimum_possible_dt;
            }
            if (dtau_a > .999*FLAGDOUBLE)
            {
                // It's possible to coalesce in population "a",
                // and the exponential term in the right-hand side of the
                // transformation for dtau_a may safely be ignored.
                // For s>0, (exp(-s*dt)-1)/(-s) is maximal at s=0.
                dt_a = dtau_a*(theta_A0+theta_a0)/theta_a0;
                if (dtau_A <= .999*FLAGDOUBLE || dt_a < dt_A)
                {
                    *pChosenPopulation = 1;
                    return dt_a;
                }
            }
            // Recall a coalescence must be possible at least one of these
            // populations, per this function's definition.
            *pChosenPopulation = 0;
            return dt_A;
        }
    }

    if (s < 0.0)
    {
        // These overflow and almost-overflow cases are analogous to those above.
        if (-s*t_s > EXPMAX - ln_theta_A0)
        {
            if (dtau_a > .999*FLAGDOUBLE)
            {
                *pChosenPopulation = 1;
                return minimum_possible_dt;
            }
            *pChosenPopulation = 0;
            return dtau_A*(theta_A0+theta_a0)/theta_A0;
        }

        if (-s*t_s > 4.0*LOG10 + ln_theta_a0 - ln_theta_A0)
        {
            if (dtau_a > .999*FLAGDOUBLE)
            {
                b = SafeProductWithExp(theta_A0,-s*t_s); // guaranteed > 0
                c = -dtau_a*(theta_A0+theta_a0); // c < 0
                x = s*c/b;
                if (x > 1.0e-04)
                    dt_a = (1.0/(-s))*log(1.0 + x);
                else
                    dt_a = x/(-s); // because log(1+x) = x + O(x^2)
                if (dt_a < minimum_possible_dt)
                    dt_a = minimum_possible_dt;
            }
            if (dtau_A > .999*FLAGDOUBLE)
            {
                dt_A = dtau_A*(theta_A0+theta_a0)/theta_A0;
                if (dtau_a <= .999*FLAGDOUBLE || dt_A < dt_a)
                {
                    *pChosenPopulation = 0;
                    return dt_A;
                }
            }
            // Recall a coalescence must be possible at least one of these
            // populations, per this function's definition.
            *pChosenPopulation = 1;
            return dt_a;
        }
    }

    // If we get here, then solve for dt_A and/or dt_a
    // by iterating over a series of increasingly better guesses.
    if (s != 0.0)
    {
        double initialGuess_dt, discriminant;
        long nIter, maxIter(1000);

        if (dtau_A > .999*FLAGDOUBLE)
        {
            // It's possible to coalesce in population "A".

            if (0.5*s*t_s > 1.0e-04 || 0.5*(-s)*t_s > 1.0e-04)
                b = SafeProductWithExp(theta_a0,s*t_s);
            // s>0: 0 < b <= 10000*theta_A0.  s<0: b >= (1.0e-04)*theta_A0.
            else
                b = theta_a0*(1.0 + s*t_s);
            a = 0.5 * b * s; // sgn(a)=sgn(s). Can't overflow. Can underflow
            // if s almost underflows.
            b += theta_A0; // b > 0
            c = -dtau_A*(theta_A0+theta_a0); // c < 0
            discriminant = b*b - 4.0*a*c; // > 0 if s > 0
            if (a > 0.0 && 1.0/a < DBL_BIG)
                initialGuess_dt = (-b + sqrt(discriminant))/(2.0*a);
            else if (a < 0.0 && -1.0/a < DBL_BIG)
            {
                if (discriminant >= 0.0)
                    initialGuess_dt = (-b + sqrt(discriminant))/(2.0*a);
                else
                    initialGuess_dt = -c/theta_A0; // heuristic guess
            }
            else // s is extremely tiny, equivalent to s=0
            {
                initialGuess_dt = dtau_A;
                maxIter = 0; // prevent the iterative loop from being executed
            }

            // Try Newton-Raphson:  solve f(x) = 0 for x, where x is dt.
            double xL = 0, xR, dx, B = (b - theta_A0)/s, f, df;
            // long numTimesOutOfBounds = 0; // for debugging

            if (s > 0)
                xR = (EXPMAX + log(s) - ln_theta_a0)/s - t_s;
            else
                xR = 2.0; // this should be big enough
            dx = xR - xL;
            x = initialGuess_dt;
            if (x <= xL || x >= xR)
                x = 0.5*(xL+xR);
            nIter = 0;

            do {
                df = SafeProductWithExp(s*B, s*x) + theta_A0;
                f = (df-theta_A0)/s - B + theta_A0*x + c;
                dx = f/df; // denominator is guaranteed nonzero
                x -= dx;
                if (x < xL)
                {
                    if (s < 0)
                        x = xL;
                    else
                    {
                        // erynes BUGBUG DEBUG: revisit this prior to releasing
                        string msg = "variable out of bounds; t_s = ";
                        msg += ToString(t_s);
                        msg += ", dtau_A = " + ToString(dtau_A) + ", s = ";
                        msg += ToString(s);
                        msg += ", f = " + ToString(f) + ", df = " + ToString(df);
                        msg += ", prev. x = " + ToString(x+dx) + ", new x = ";
                        msg += ToString(x);
                        msg += ", nIter = " + ToString(nIter);
                        throw impossible_error(msg);
                    }
                }
                else if (x > xR)
                {
                    if (s > 0)
                        x = xR;
                    else
                    {
                        // erynes BUGBUG DEBUG: revisit this prior to releasing
                        string msg = "variable out of bounds; t_s = ";
                        msg += ToString(t_s);
                        msg += ", dtau_A = " + ToString(dtau_A) + ", s = ";
                        msg += ToString(s);
                        msg += ", f = " + ToString(f) + ", df = " + ToString(df);
                        msg += ", prev. x = " + ToString(x+dx) + ", new x = ";
                        msg += ToString(x);
                        msg += ", nIter = " + ToString(nIter);
                        throw impossible_error(msg);
                    }
                }
                nIter++;
            } while (fabs(dx/x) > 0.00001 && nIter < maxIter);

            if (nIter < maxIter)
                dt_A = x;
            else if (0 == maxIter)
                dt_A = dtau_A;
            else
                dt_A = MAX_LENGTH;  // BUG? -- danger probably too low, try DBL_BIG
                                    // and make sure that any matching code trying
                                    // to detect what kind of horserace conditions
                                    // these actually are is also changed
        }

        if (dtau_a > .999*FLAGDOUBLE)
        {
            // It's possible to coalesce in population "a".

            maxIter = 1000;

            if (0.5*s*t_s > 1.0e-04 || 0.5*(-s)*t_s > 1.0e-04)
            {
                // If we have just completed computing dt_A, re-use one
                // computation.
                if (dtau_A > .999*FLAGDOUBLE)
                    b = theta_A0*theta_a0/(b-theta_A0);
                // b = SafeProductWithExp(theta_A0, -s*t_s)
                else
                    b = SafeProductWithExp(theta_A0, -s*t_s);
                // s>0: b >= (1.0e-04)*theta_a0.  s<0: 0 < b <= 10000*theta_a0.
            }
            else
                b = theta_A0*(1.0 - s*t_s);
            a = -0.5 * b * s; // sgn(a) = -sgn(s). Can't overflow.
            // Can underflow if s almost underflows.
            b += theta_a0; // b > 0
            c = -dtau_a*(theta_A0+theta_a0); // c < 0
            discriminant = b*b - 4.0*a*c; // > 0 if s < 0
            if (a > 0.0 && 1.0/a < DBL_BIG)
                initialGuess_dt = (-b + sqrt(discriminant))/(2.0*a);
            else if (a < 0.0 && -1.0/a < DBL_BIG)
            {
                if (discriminant >= 0.0)
                    initialGuess_dt = (-b + sqrt(discriminant))/(2.0*a);
                else
                    initialGuess_dt = -c/theta_a0; // heuristic guess
            }
            else // s is extremely tiny, equivalent to s=0
            {
                initialGuess_dt = dtau_a;
                maxIter = 0; // prevent either iterative loop from being executed
            }

            // Try Newton-Raphson:  solve f(x) = 0 for x, where x is dt.
            double xL = 0, xR, dx, B = (b - theta_a0)/(-s), f, df;
            //long numTimesOutOfBounds = 0; // for debugging

            if (s < 0)
                xR = (EXPMAX + log(-s) - ln_theta_A0)/(-s) - t_s;
            else
                xR = 2.0; // this should be big enough
            dx = xR - xL;
            x = initialGuess_dt;
            if (x <= xL || x >= xR)
                x = 0.5*(xL+xR);
            nIter = 0;

            do {
                df = SafeProductWithExp(-s*B, -s*x) + theta_a0;
                f = (df-theta_a0)/(-s) - B + theta_a0*x + c;
                dx = f/df; // guaranteed nonzero
                x -= dx;
                if (x < xL)
                {
                    if (s > 0)
                        x = xL;
                    else
                    {
                        // erynes BUGBUG DEBUG: revisit this prior to releasing
                        string msg = "variable out of bounds; t_s = ";
                        msg += ToString(t_s);
                        msg += ", dtau_a = " + ToString(dtau_a) + ", s = ";
                        msg += ToString(s);
                        msg += ", f = " + ToString(f) + ", df = " + ToString(df);
                        msg += ", prev. x = " + ToString(x+dx) + ", new x = ";
                        msg += ToString(x);
                        msg += ", nIter = " + ToString(nIter);
                        throw impossible_error(msg);
                    }
                }
                else if (x > xR)
                {
                    if (s < 0)
                        x = xR;
                    else
                    {
                        // erynes BUGBUG DEBUG: revisit this prior to releasing
                        string msg = "variable out of bounds; t_s = ";
                        msg += ToString(t_s);
                        msg += ", dtau_a = " + ToString(dtau_a) + ", s = ";
                        msg += ToString(s);
                        msg += ", f = " + ToString(f) + ", df = " + ToString(df);
                        msg += ", prev. x = " + ToString(x+dx) + ", new x = ";
                        msg += ToString(x);
                        msg += ", nIter = " + ToString(nIter);
                        throw impossible_error(msg);
                    }
                }
                nIter++;
            } while (fabs(dx/x) > 0.00001 && nIter < maxIter);

            if (nIter < maxIter)
                dt_a = x;
            else if (0 == maxIter)
                dt_a = dtau_a;
            else
                dt_a = MAX_LENGTH;  // BUG? -- danger probably too low, try DBL_BIG
                                    // and make sure that any matching code trying
                                    // to detect what kind of horserace conditions
                                    // these actually are is also changed
        }

    } // end of the iterative solution code

    else // s == 0, and "magic time" is actually "real time"
    {
        dt_A = dtau_A;
        dt_a = dtau_a;
    }

    if ((dt_A >= 0.0 && dt_A < minimum_possible_dt) ||
        (dt_a >= 0.0 && dt_a < minimum_possible_dt))
    {
        string msg = "Bad time length (" + ToString(std::min(dt_A,dt_a));
        msg += ") proposed in LogisticSelectionPickTime().";
        tinypopulation_error e(msg);
        throw e;
    }
    if (dt_a < 0.0) // FLAGDOUBLE
    {
        *pChosenPopulation = (dt_A < 0.0) ? FLAGLONG : 0;
        return dt_A;
    }
    if (dt_A < 0.0) // FLAGDOUBLE
    {
        *pChosenPopulation = 1;
        return dt_a;
    }
    // If we get here, both interval lengths are valid.
    if (dt_A < dt_a)
    {
        *pChosenPopulation = 0;
        return dt_A;
    }
    *pChosenPopulation = 1;
    return dt_a;

} // LogisticSelectionPickTime

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

double LogSelectTimeManager::TimeOfInactiveCoal(double tstart,
                                                const LongVec1d& activelines, const LongVec1d& inactivelines,
                                                const ForceParameters& fp, long& targetxpart, double)
{
    DoubleVec1d thetas = fp.GetRegionalThetas();
    double selectionCoefficient = fp.GetLogisticSelectionCoefficient()[0];
    long nxparts = thetas.size();
    if (2 != nxparts)
    {
        string msg = "InactiveLogisticSelectionCoal::PickTime() called ";
        msg += "with nxparts = ";
        msg += ToString(nxparts);
        msg += ".  nxparts must equal 2, reflecting one population with ";
        msg += "the major allele and one population with the minor allele.";
        throw implementation_error(msg);
    }

    if (0.0 == thetas[0] || 0.0 == thetas[1])
    {
        string msg = "InactiveLogisticSelectionCoal::PickTime() received an ";
        msg += "invalid Theta of 0.0.";
        throw impossible_error(msg);
    }

    if (!((activelines[0] >= 1 && inactivelines[0] >= 1) ||
          (activelines[1] >= 1 && inactivelines[1] >= 1)))
    {
        // no event is possible
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }

    // First, compute the interval lengths in "magic time"--drawn from an
    // exponential distribution assuming constant size for population "A"
    // and for population "a".
    double dtau_A(FLAGDOUBLE), dtau_a(FLAGDOUBLE); // important:  these are < 0

    if (activelines[0] > 0 && inactivelines[0] > 0)
        dtau_A = -log(randomSource.Float()) /
            (2.0*activelines[0]*inactivelines[0]/thetas[0]);

    if (activelines[1] > 0 && inactivelines[1] > 0)
        dtau_a = -log(randomSource.Float()) /
            (2.0*activelines[1]*inactivelines[1]/thetas[1]);

    return tstart + LogisticSelectionPickTime(thetas[0],thetas[1],selectionCoefficient,
                                              tstart,dtau_A,dtau_a,&targetxpart);

} // TimeOfInactiveCoal

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

double LogSelectTimeManager::TimeOfTraitMutation(double tstart,
                                                 const LongVec1d& lineages, const ForceParameters& fp, long& tiptrait,
                                                 long& roottrait, double)
{

    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart
    DoubleVec2d murates(fp.GetRegional2dRates(force_DISEASE));
    if (2 != murates[0].size() || 2 != murates[1].size() ||
        0 != murates[0][0] || 0 != murates[1][1] || 0 >= murates[0][1] ||
        0 >= murates[1][0])
    {
        string msg("LogSelectTimeManager::TimeOfTraitMutation():  ");
        msg += "bad murates matrix";
        throw implementation_error(msg);
    }

    double mu_into_A_from_a(murates[0][1]);
    double mu_into_a_from_A(murates[1][0]);
    double s(fp.GetLogisticSelectionCoefficient()[0]); // one-element vector
    double theta_A0(fp.GetGlobalThetas()[0]);
    double theta_a0(fp.GetGlobalThetas()[1]);
    double dtau_A(FLAGDOUBLE), dtau_a(FLAGDOUBLE), dt_A(FLAGDOUBLE),
        dt_a(FLAGDOUBLE), argOfLog;

    if (lineages[0] > 0)
    {
        dtau_A = -log(randomSource.Float()) /
            (mu_into_A_from_a * lineages[0]);
        if (0.0 == s)
            dt_A = dtau_A;
        else
        {
            argOfLog = 1.0 +
                SafeProductWithExp((dtau_A*s*theta_A0/theta_a0),-s*tstart);
            if (argOfLog > 0.0)
                dt_A = log(argOfLog) / s;
        }
    }
    if (lineages[1] > 0)
    {
        dtau_a = -log(randomSource.Float()) /
            (mu_into_a_from_A * lineages[1]);
        if (0.0 == s)
            dt_a = dtau_a;
        else
        {
            argOfLog = 1.0 +
                SafeProductWithExp((dtau_a*(-s)*theta_a0/theta_A0),s*tstart);
            if (argOfLog > 0.0)
                dt_a = log(argOfLog) / (-s);
        }
    }

    if (dt_a <= 0.0)
    {
        if (dt_A <= 0.0)
        {
            // no event is possible
            tiptrait = FLAGLONG;
            roottrait = FLAGLONG;
            return FLAGDOUBLE;
        }
        tiptrait = 0; // going backward in time, from tip to root
        roottrait = 1;
        return dt_A;
    }

    // If we get here, dt_a > 0.
    if (dt_A <= 0.0 || dt_A > dt_a)
    {
        tiptrait = 1;
        roottrait = 0;
        return tstart + dt_a;
    }

    // If we get here, 0 < dt_A <= dt_a.
    tiptrait = 0;
    roottrait = 1;
    return tstart + dt_A;

} // TimeOfTraitMutation

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

TimeManager& StickTimeManager::operator=(const TimeManager& src)
{
    TimeManager::operator=(src);
    m_stair = dynamic_cast<const StickTimeManager&>(src).m_stair;
    return *this;
} // TimeManager operator=

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

void StickTimeManager::CopyStick(const TimeManager& src)
{
    m_stair = dynamic_cast<const StickTimeManager&>(src).m_stair;
} // StickTimeManager::UsingStick

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

bool StickTimeManager::UsingStick() const
{
    return true;
} // StickTimeManager::UsingStick

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

void StickTimeManager::ClearStick()
{
    m_stair.clear();
} // StickTimeManager::ClearStick

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

void StickTimeManager::SetStickParameters(const ForceParameters& fp)
{
    if (m_stair.empty())
    {
        // We don't need to do anything!?!
    }
    else
    {
        DoubleVec1d newthetas(fp.GetRegionalThetas());
        DoubleVec1d oldthetas(m_stair.begin()->GetThetas());
        if (oldthetas != newthetas)
        {
            double ratio(std::accumulate(newthetas.begin(),newthetas.end(), 0.0) /
                         std::accumulate(oldthetas.begin(),oldthetas.end(), 0.0));
            stair::iterator step;
            for(step = m_stair.begin(); step != m_stair.end(); ++step)
            {
                DoubleVec1d ths(step->GetThetas());
                std::transform(ths.begin(),ths.end(),ths.begin(),
                               bind2nd(std::multiplies<double>(),ratio));
                step->SetThetas(ths);
            }
        }
    }
} // StickTimeManager::SetStickParameters

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

void StickTimeManager::ScoreStick(TreeSummary& treesum) const
{
    // NB: I don't think it's ever right for the stick to be
    // empty here; why were we trying to sample it, and how could
    // it be empty?  Adding an assert to test.

    assert(!m_stair.empty());

    DoubleVec2d stickfreqs;
    DoubleVec1d sticklengths;
    DoubleVec1d tipfreqs;
    DoubleVec2d sticklnfreqs;

    stair::const_iterator br;
    for(br = m_stair.begin(); br != m_stair.end(); ++br)
    {
        DoubleVec1d thetas(br->GetThetas());
        DoubleVec1d freqs(thetas.size());
        DoubleVec1d ratios(thetas.size());
        DoubleVec1d lnfreqs(thetas.size());
        double zero(0.0);
        std::transform(thetas.begin(),thetas.end(),freqs.begin(),
                       std::bind2nd(std::divides<double>(),
                                    std::accumulate(thetas.begin(),thetas.end(),zero)));
        stickfreqs.push_back(freqs);
        double newlength(br->GetRootendTime() - br->GetTipendTime());
        sticklengths.push_back(newlength);

        if (br == m_stair.begin()) tipfreqs = freqs;

        lnfreqs = freqs;
        DoubleVec1d::iterator frit;
        for(frit = lnfreqs.begin(); frit != lnfreqs.end(); ++frit)
            (*frit) = log(*frit);
        sticklnfreqs.push_back(lnfreqs);
    }

    treesum.SetStickSummary(stickfreqs,sticklengths,sticklnfreqs);

} // StickTimeManager::ScoreStick

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

void StickTimeManager::ChopOffStickAt(double lasttime)
{
    if (m_stair.empty()) return;

    stair::iterator cuthere(m_stair.end());
    --cuthere;
    while(cuthere->GetTipendTime() > lasttime) --cuthere;
    ++cuthere;
    if (cuthere == m_stair.end()) return;

    m_stair.erase(cuthere,m_stair.end());

} // StickTimeManager::ChopOffStickAt

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

void StickTimeManager::MakeStickUsingBranches(const ForceParameters& fp,
                                              const std::vector<std::pair<double,LongVec1d> >& lpcounts)
{
    ClearStick();

    // add the first joint back in
    AppendRiser(fp);

    double timeprev(0.0),timenow(m_stair.begin()->GetRootendTime());
    double lasteventtime(lpcounts.back().first);
    std::vector<std::pair<double,LongVec1d> >::const_iterator
        lpnow(lpcounts.begin());
    std::vector<std::pair<double,LongVec1d> >::const_iterator lpnext(lpnow);
    ++lpnext;
    while(timenow < lasteventtime)
    {
        // average branch counts over previous joint
        DoubleVec1d discounts;
        discounts.assign(lpnow->second.begin(),lpnow->second.end());
        DoubleVec1d oneless(discounts);
        std::transform(oneless.begin(),oneless.end(),oneless.begin(),
                       std::bind2nd(std::minus<double>(),1.0));
        std::transform(discounts.begin(),discounts.end(),oneless.begin(),
                       discounts.begin(),std::multiplies<double>());

        // weight each total by the fraction of the time it represents
        double tstart(std::max(lpnow->first,timeprev));
        double tend(std::min(lpnext->first,timenow));
        double weight((tend-tstart)/(timenow-timeprev));
        std::transform(discounts.begin(),discounts.end(),discounts.begin(),
                       std::bind2nd(std::multiplies<double>(),weight));

        long nevents(1);
        while(lpnext->first < timenow)
        {
            ++lpnow;
            ++lpnext;
            ++nevents;
            DoubleVec1d total(lpnow->second.begin(),lpnow->second.end());
            oneless = total;
            std::transform(oneless.begin(),oneless.end(),oneless.begin(),
                           std::bind2nd(std::minus<double>(),1.0));
            std::transform(total.begin(),total.end(),oneless.begin(),
                           total.begin(),std::multiplies<double>());

            // weight each total by the fraction of the time it represents
            tstart = std::max(lpnow->first,timeprev);
            tend = std::min(lpnext->first,timenow);
            weight = (tend-tstart)/(timenow-timeprev);
            std::transform(total.begin(),total.end(),total.begin(),
                           std::bind2nd(std::multiplies<double>(),weight));

            std::transform(discounts.begin(),discounts.end(),total.begin(),
                           discounts.begin(),std::plus<double>());
        }

        std::transform(discounts.begin(),discounts.end(),discounts.begin(),
                       std::bind2nd(std::divides<double>(),nevents));

        AppendRiser(fp,discounts);

        timeprev = timenow;
        timenow = m_stair.back().GetRootendTime();
    }

} // StickTimeManager::MakeStickUsingBranches

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

void StickTimeManager::MakeStickTilTime(const ForceParameters& fp, double endtime)
{
    ClearStick();

    // add the first joint back in
    AppendRiser(fp);

    double timenow(m_stair.begin()->GetRootendTime());
    while(timenow < endtime)
    {
        AppendRiser(fp);
        timenow = m_stair.back().GetRootendTime();
    }

} // StickTimeManager::MakeStickTilTime

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickThetasToFile(std::ofstream& of) const
{
    stair::const_iterator br;
    for(br = m_stair.begin(); br != m_stair.end(); ++br)
    {
        of << "stick-thetas:";
        DoubleVec1d thetas(br->GetThetas());
        std::copy(thetas.begin(),thetas.end(),
                  std::ostream_iterator<double>(of, " "));
        of << std::endl;
    }
} // StickTimeManager::PrintStickThetasToFile

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickFreqsToFile(std::ofstream& of) const
{
    of << m_stair.size() << std::endl;
    stair::const_iterator br;
    for(br = m_stair.begin(); br != m_stair.end(); ++br)
    {
        of << "stick-freqs:";
        DoubleVec1d freqs(br->GetThetas());
        double totaltheta(std::accumulate(freqs.begin(),freqs.end(), 0.0));
        std::transform(freqs.begin(),freqs.end(),freqs.begin(),
                       std::bind2nd(std::divides<double>(),totaltheta));
        std::copy(freqs.begin(),freqs.end(),
                  std::ostream_iterator<double>(of, " "));
        of << std::endl;
    }
} // StickTimeManager::PrintStickFreqsToFile

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickFreqsToFileAtTime(std::ofstream& of, double time) const
{
    stair::const_iterator br;
    for(br = m_stair.begin(); br != m_stair.end(); ++br)
    {
        if (br->GetRootendTime() > time)
        {
            of << "stick-freqs at time " << time << ":";
            DoubleVec1d freqs(br->GetThetas());
            double totaltheta(std::accumulate(freqs.begin(),freqs.end(), 0.0));
            std::transform(freqs.begin(),freqs.end(),freqs.begin(),
                           std::bind2nd(std::divides<double>(),totaltheta));
            std::copy(freqs.begin(),freqs.end(),
                      std::ostream_iterator<double>(of, " "));
            of << std::endl;
            break;
        }
    }

} // StickTimeManager::PrintStickFreqsToFileAtTime

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickThetasToFileForJoint300(std::ofstream& of) const
{
    if (m_stair.size() < 300) return;

    long count;
    stair::const_iterator br;
    of << "stick-thetas:";
    for(br = m_stair.begin(), count=0; br != m_stair.end() && count < 300;
        ++br, ++count) ;

    assert(count == 300);

    DoubleVec1d thetas(br->GetThetas());
    std::copy(thetas.begin(),thetas.end(),
              std::ostream_iterator<double>(of, " "));
    of << std::endl;

} // StickTimeManager::PrintStickThetasToFileForJoint300

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickFreqsToFileForJoint300(std::ofstream& of) const
{
    if (m_stair.size() < 300) return;

    long count;
    stair::const_iterator br;
    of << "stick-freqs:";
    for(br = m_stair.begin(), count=0; br != m_stair.end() && count < 300;
        ++br, ++count) ;

    assert(count == 300);

    DoubleVec1d freqs(br->GetThetas());
    double totaltheta(std::accumulate(freqs.begin(),freqs.end(), 0.0));
    std::transform(freqs.begin(),freqs.end(),freqs.begin(),
                   std::bind2nd(std::divides<double>(),totaltheta));
    std::copy(freqs.begin(),freqs.end(),
              std::ostream_iterator<double>(of, " "));
    of << std::endl;

} // StickTimeManager::PrintStickFreqsToFileForJoint300

//------------------------------------------------------------------------------------
// Debugging function.

void StickTimeManager::PrintStickToFile(std::ofstream& of) const
{
    long maxjoints(120);

    long count(0);
    stair::const_iterator br(m_stair.begin());
    stair::const_iterator next(br);
    ++next;

    // we add a cap of 50 joints to avoid too big files...
    for(; next != m_stair.end() && count < maxjoints; ++br, ++next, ++count)
    {
        DoubleVec1d freqs(br->GetThetas());
        double totaltheta(std::accumulate(freqs.begin(),freqs.end(), 0.0));
        std::transform(freqs.begin(),freqs.end(),freqs.begin(),
                       std::bind2nd(std::divides<double>(),totaltheta));
        of << "joint#" << count << " ";
        of << "freqA=" << freqs[0] << " :lnfreqA=";
        of << log(freqs[0]) << ";" << " with length=";
        of << next->GetTipendTime() - br->GetTipendTime() << std::endl;
    }
}

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

DoubleVec1d StickTimeManager::XpartThetasAtT(double t, const ForceParameters& fp)
{
    DoubleVec1d thetas(FindJointAtTime(t,fp)->GetThetas());
    return thetas;
} // StickTimeManager::XpartThetasAtT

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

DoubleVec1d StickTimeManager::PartitionThetasAtT(double t, force_type partforce, const ForceParameters& fp)
{
    DoubleVec1d thetas(FindJointAtTime(t,fp)->GetThetas());
    return dynamic_cast<PartitionForce*>(*registry.GetForceSummary().
                                         GetForceByTag(partforce))->SumXPartsToParts(thetas);

} // StickTimeManager::PartitionThetasAtT

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

double StickTimeManager::TimeOfActiveCoal(double tstart,
                                          const LongVec1d& lineages, const ForceParameters& fp, long& targetxpart,
                                          double maxtime)
{
    // Computes the timestamp of the end of the interval by means of computing "delta t" and adding it to tstart.

    // If there are no populations with multiple lineages, coalescence is impossible, so return immediately.
    if (*max_element(lineages.begin(),lineages.end()) < 2)
    {
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }

    stair::iterator joint(FindJointAtTime(tstart,fp));
    DoubleVec1d::size_type nxparts = fp.GetRegionalThetas().size();

    DoubleVec1d newfrac(nxparts);
    DoubleVec1d::iterator it;
    for(it = newfrac.begin(); it != newfrac.end(); ++it)
    {
        // technically, we should be taking the log of (1-rnd[float]),
        // but that's the same as log(rnd[float]), so omitted for
        // simplicity/speed
        (*it) = -1.0 * log(randomSource.Float());
    }

    DoubleVec1d total(nxparts, 0.0), oldtotal(nxparts, 0.0);
    double length(joint->GetRootendTime() - tstart), totallength(0.0);

    double maxdisplace(maxtime - tstart);

    while(true)
    {
        oldtotal = total;
        DoubleVec1d thetas(joint->GetThetas());

        assert(thetas.size() == newfrac.size());

        double mindisplace(DBL_MAX);
        DoubleVec1d::size_type i;
        for (i = 0; i < nxparts; ++i)
        {
            if (lineages[i] < 2) continue;
            total[i] += lineages[i] * (lineages[i] - 1.0) /
                thetas[i] * length;
            if (total[i] > newfrac[i])
            {
                double newdisplace(newfrac[i] - oldtotal[i]);
                newdisplace /= lineages[i] * (lineages[i] - 1.0) / thetas[i];
                newdisplace += totallength;
                if (newdisplace < mindisplace)
                {
                    targetxpart = i;
                    if (newdisplace > MAX_LENGTH)
                        newdisplace = MAX_LENGTH;
                    // BUG? -- danger probably too low, try DBL_BIG
                    // and make sure that any matching code trying
                    // to detect what kind of horserace conditions
                    // these actually are is also changed
                    mindisplace = newdisplace;
                }
            }
        }
        if (mindisplace < DBL_MAX) return tstart + mindisplace;
        totallength += length;
        if (totallength >= maxdisplace)
        {
            targetxpart = FLAGLONG;
            return FLAGDOUBLE;
        }

        joint = NextJointAppendingIfLast(joint,fp);
        length = joint->GetRootendTime() - joint->GetTipendTime();
    }

    // can't get here
    assert(false);
    targetxpart = FLAGLONG;
    return FLAGDOUBLE;

} // StickTimeManager::TimeOfActiveCoal

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

bool StickTimeManager::InactiveCoalImpossible(const LongVec1d& alines, const LongVec1d& ilines) const
{
    // JCHECK
    assert(alines.size() == ilines.size());

    LongVec1d::size_type xpart;
    for(xpart = 0; xpart < alines.size(); ++xpart)
        if (ilines[xpart] > 0 && alines[xpart] > 0) return false;

    return true;

} // StickTimeManager::InactiveCoalImpossible

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

double StickTimeManager::TimeOfInactiveCoal(double tstart,
                                            const LongVec1d& activeline, const LongVec1d& inactivelines,
                                            const ForceParameters& fp, long& targetxpart, double maxtime)
{
    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart
    // if no such coalescences are legal, return immediately
    if (InactiveCoalImpossible(activeline,inactivelines))
    {
        targetxpart = FLAGLONG;
        return FLAGDOUBLE;
    }

    stair::iterator joint(FindJointAtTime(tstart,fp));
    DoubleVec1d::size_type nxparts = fp.GetRegionalThetas().size();
    DoubleVec1d newfrac(nxparts);
    DoubleVec1d::iterator it;
    for(it = newfrac.begin(); it != newfrac.end(); ++it)
        (*it) = -1.0 * log(randomSource.Float());

    DoubleVec1d total(nxparts, 0.0), oldtotal(nxparts, 0.0);
    double length(joint->GetRootendTime() - tstart), totallength(0.0);

    double maxdisplace(maxtime - tstart);

    while(true)
    {
        oldtotal = total;
        DoubleVec1d thetas(joint->GetThetas());

        assert(thetas.size() == newfrac.size());

        double mindisplace(DBL_MAX);
        DoubleVec1d::size_type i;
        for (i = 0; i < nxparts; ++i)
        {
            if (inactivelines[i] < 1 || activeline[i] < 1) continue;
            total[i] += 2.0 * activeline[i] * inactivelines[i] /
                thetas[i] * length;
            if (total[i] > newfrac[i])
            {
                double newdisplace(newfrac[i] - oldtotal[i]);
                newdisplace /= 2.0 * activeline[i] * inactivelines[i] /
                    thetas[i];
                newdisplace += totallength;
                if (newdisplace < mindisplace)
                {
                    targetxpart = i;
                    if (newdisplace > MAX_LENGTH)
                        newdisplace = MAX_LENGTH;
                    // BUG? -- danger probably too low, try DBL_BIG
                    // and make sure that any matching code trying
                    // to detect what kind of horserace conditions
                    // these actually are is also changed
                    mindisplace = newdisplace;
                }
            }
        }

        if (mindisplace < DBL_MAX) return tstart + mindisplace;

        totallength += length;

        if (totallength >= maxdisplace)
        {
            targetxpart = FLAGLONG;
            return FLAGDOUBLE;
        }

        joint = NextJointAppendingIfLast(joint,fp);
        length = joint->GetRootendTime() - joint->GetTipendTime();
    }

    // can't get here
    assert(false);
    targetxpart = FLAGLONG;
    return FLAGDOUBLE;

} // StickTimeManager::TimeOfInactiveCoal

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

bool StickTimeManager::MutationImpossible(const LongVec1d& lineages,
                                          const DoubleVec1d& mus) const
{
    assert(mus.size() == lineages.size());
    LongVec1d::size_type part;
    for(part = 0; part < lineages.size(); ++part)
        if (lineages[part] * mus[part] > 0.0) return false;

    return true;

} // StickTimeManager::MutationImpossible

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

double StickTimeManager::TimeOfTraitMutation(double tstart,
                                             const LongVec1d& lineages, const ForceParameters& fp, long& tiptrait,
                                             long& roottrait, double maxtime)
{
    // Computes the timestamp of the end of the interval by means of
    // computing "delta t" and adding it to tstart
    DoubleVec1d mus(fp.GetOnlyDiseaseRates());
    if (MutationImpossible(lineages,mus))
    {
        tiptrait = FLAGLONG;
        roottrait = FLAGLONG;
        return FLAGDOUBLE;
    }

    DoubleVec1d::size_type nparts = mus.size();
    DoubleVec1d newfrac(nparts);
    DoubleVec1d::iterator it;
    for(it = newfrac.begin(); it != newfrac.end(); ++it)
    {
        // technically, we should be taking the log of (1-rnd[float]),
        // but that's the same as log(rnd[float]), so omitted for
        // simplicity/speed
        (*it) = -1.0 * log(randomSource.Float());
    }

    DoubleVec1d total(nparts, 0.0), oldtotal(nparts, 0.0);
    stair::iterator joint(FindJointAtTime(tstart,fp));
    double length(joint->GetRootendTime() - tstart), totallength(0.0);

    double maxdisplace(maxtime - tstart);
    assert(nparts == 2.0);

    while(true)
    {
        oldtotal = total;
        DoubleVec1d thetas(joint->GetThetas());

        assert(thetas.size() == 2);

        double mindisplace(DBL_MAX);

        // unrolling the loop since we only have 2 partitions and
        // avoid partition/xpartition mapping by doing so....
        if (lineages[0]*mus[0] > 0)
        {
            total[0] += lineages[0] * mus[0] * thetas[1] / thetas[0] * length;
            if (total[0] > newfrac[0])
            {
                double newdisplace(newfrac[0] - oldtotal[0]);
                newdisplace /= lineages[0] * mus[0] * thetas[1] / thetas[0];
                newdisplace += totallength;
                if (newdisplace < mindisplace)
                {
                    tiptrait = 0;
                    roottrait = 1;
                    mindisplace = newdisplace;
                }
            }
        }

        if (lineages[1]*mus[1] > 0)
        {
            total[1] += lineages[1] * mus[1] * thetas[0] / thetas[1] * length;
            if (total[1] > newfrac[1])
            {
                double newdisplace(newfrac[1] - oldtotal[1]);
                newdisplace /= lineages[1] * mus[1] * thetas[0] / thetas[1];
                newdisplace += totallength;
                if (newdisplace < mindisplace)
                {
                    tiptrait = 1;
                    roottrait = 0;
                    mindisplace = newdisplace;
                }
            }
        }

        if (mindisplace != DBL_MAX && mindisplace > MAX_LENGTH)
            mindisplace = MAX_LENGTH;
        // BUG? -- danger probably too low, try DBL_BIG
        // and make sure that any matching code trying
        // to detect what kind of horserace conditions
        // these actually are is also changed

#if 0
        DoubleVec1d::size_type i;
        for (i = 0; i < nparts; ++i)
        {
            if (lineages[i] < 1) continue;
            total[i] += lineages[i] * (lineages[i] - 1.0) /
                thetas[i] * length;
            if (total[i] > newfrac[i])
            {
                double newdisplace(newfrac[i] - oldtotal[i]);
                newdisplace /= lineages[i] * (lineages[i] - 1.0) / thetas[i];
                newdisplace += totallength;
                if (newdisplace < mindisplace)
                {
                    targetxpart = i;
                    if (mindisplace > MAX_LENGTH)
                        mindisplace = MAX_LENGTH;
                    // BUG? -- danger probably too low, try DBL_BIG
                    // and make sure that any matching code trying
                    // to detect what kind of horserace conditions
                    // these actually are is also changed
                    mindisplace = newdisplace;
                }
            }
        }
#endif

        if (mindisplace < DBL_MAX) return tstart + mindisplace;
        totallength += length;
        if (totallength >= maxdisplace)
        {
            tiptrait = FLAGLONG;
            roottrait = FLAGLONG;
            return FLAGDOUBLE;
        }

        joint = NextJointAppendingIfLast(joint,fp);
        length = joint->GetRootendTime() - joint->GetTipendTime();
    }

    // can't get here
    assert(false);
    tiptrait = FLAGLONG;
    roottrait = FLAGLONG;
    return FLAGDOUBLE;

} // StickTimeManager::TimeOfTraitMutation

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

stair::iterator StickTimeManager::FindJointAtTime(double time, const ForceParameters& fp)
{
    stair::iterator joint;
    for (joint = m_stair.begin(); joint != m_stair.end(); ++joint)
    {
        if (time < joint->GetRootendTime()) return joint;
    }

    // if we got here, the stick ran out too soon
    AppendRiser(fp);
    --joint;
    while(time >= joint->GetRootendTime())
    {
        AppendRiser(fp);
        ++joint;
    }
    return joint;

} // StickTimeManager::FindJointAtTime

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

stair::iterator StickTimeManager::NextJointAppendingIfLast(
    const stair::iterator& start, const ForceParameters& fp)
{
    stair::iterator joint(start);
    ++joint;
    if (joint == m_stair.end())
    {
        AppendRiser(fp);
        --joint;
    }

    return joint;

} // StickTimeManager::NextJointAppendingIfLast

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

void StickTimeManager::AppendRiser(const ForceParameters& fp)
{
    double newtiptime((m_stair.empty() ? 0.0 : m_stair.back().GetRootendTime()));
    double newroottime(NextRootTime(newtiptime,fp));
    DoubleVec1d newthetas(NextThetas(newtiptime,fp));
    m_stair.push_back(StairRiser(newthetas,newtiptime,newroottime));

} // StickTimeManager::AppendRiser

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

void StickTimeManager::AppendRiser(const ForceParameters& fp, const DoubleVec1d& discounts)
{
    double newtiptime((m_stair.empty() ? 0.0 : m_stair.back().GetRootendTime()));
    double newroottime(NextRootTime(newtiptime,fp));
    DoubleVec1d newthetas(NextThetas(newtiptime,fp,discounts));
    m_stair.push_back(StairRiser(newthetas,newtiptime,newroottime));
} // StickTimeManager::AppendRiser(twoargs)

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

void StickTimeManager::PrintStick() const
{
    stair::const_iterator it;
    for (it = m_stair.begin(); it != m_stair.end(); ++it)
    {
        DoubleVec1d ths(it->GetThetas());
        std::cout << "Joint from " << it->GetTipendTime() <<
            " to " << it->GetRootendTime() << " ";
        std::cout << ths[0] << "/" << ths[1] << std::endl;
    }
} // PrintStick debug function

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

ExpGrowStickTimeManager::ExpGrowStickTimeManager()
    : StickTimeManager(),
      m_percentchange(defaults::perThetaChange),
      m_negln(log(1.0+m_percentchange)),m_posln(log(1.0-m_percentchange))
{
} // ExpGrowStickTimeManager default ctor

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

double ExpGrowStickTimeManager::NextRootTime(double timenow, const
                                             ForceParameters& fp) const
{
    double newtime(DBL_BIG);
    DoubleVec1d params(fp.GetGrowthRates());
    DoubleVec1d::const_iterator param;
    assert(!params.empty());  // no growth?!
    for(param = params.begin(); param != params.end(); ++param)
    {
        double ptime((*param < 0.0) ? timenow - m_negln/(*param) :
                     timenow - m_posln/(*param));
        if (ptime < newtime) newtime = ptime;
    }

    return newtime;

} // ExpGrowStickTimeManager::NextRootTime

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

DoubleVec1d ExpGrowStickTimeManager::NextThetas(double nexttime, const ForceParameters& fp) const
{
    DoubleVec1d thetas(fp.GetRegionalThetas()),growths(fp.GetGrowthRates());
    assert(thetas.size() == growths.size());
    if (nexttime == 0.0) return thetas;  // don't bother, we're at the tips
    DoubleVec1d newthetas(thetas.size());
    unsigned long param;
    for(param = 0; param < thetas.size(); ++param)
    {
        newthetas[param] = SafeProductWithExp(thetas[param], -growths[param]*nexttime);
    }

    return newthetas;

} // ExpGrowStickTimeManager::NextThetas

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

DoubleVec1d ExpGrowStickTimeManager::NextThetas(double nexttime, const ForceParameters& fp, const DoubleVec1d&) const
{
    // We ignore the third argument as it shouldn't make any difference for us.
    return NextThetas(nexttime,fp);
} // ExpGrowStickTimeManager::NextThetas

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

double SelectStickTimeManager::NextRootTime(double timenow, const ForceParameters&) const //fp unused.
{
    // JDEBUG--constant length joints for now?
    // double newlength = 0.002; // for lpl'ish data
    double newlength = 0.0015; // for theta around 1.0
    // double newlength = 0.015; // for comparison with sticktree2
    // double newlength = 0.4;  // for artificial sims
    return timenow + newlength;
} // SelectStickTimeManager::NextRootTime

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

DoubleVec1d SelectStickTimeManager::NextThetas(double nexttime, const ForceParameters& fp) const
{
    DoubleVec1d newthetas;

    if (m_stair.empty())                // first riser being built
    {
        newthetas = fp.GetRegionalThetas();
        return newthetas;
    }

    stair::const_iterator prevjoint(m_stair.end());
    if (nexttime >= m_stair.back().GetRootendTime()) // appending a new stair
    {
        --prevjoint;
        newthetas = CalculateThetasWithPrevOnly(prevjoint,fp);
    }
    else if (nexttime > m_stair.back().GetTipendTime()) // modifying last thetas
    {
        assert(false); // you should never pick the first joint...which is the
        // same as the last joint when there is only one
        // joint.
        --prevjoint;
        --prevjoint;
        newthetas = CalculateThetasWithPrevOnly(prevjoint,fp);
    }
    else
    {
        // modifying thetas with both a previous and next riser present
        // JDEBUG--can't use
        //    stair::const_iterator chosen(FindJointAtTime(nexttime,fp));
        // because it's non-const....
        stair::const_iterator chosen(m_stair.begin());
        while(chosen->GetRootendTime() < nexttime)
        {
            ++chosen;
            assert(chosen != m_stair.end()); // or even --(m_stair.end())!!
        }
        prevjoint = chosen;
        --prevjoint;
        stair::const_iterator nextjoint = chosen;
        ++nextjoint;
        newthetas = CalculateThetasWithPrevAndNext(prevjoint,chosen,nextjoint,fp);
    }

    assert(!newthetas.empty());
    return newthetas;

} // SelectStickTimeManager::NextThetas

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

DoubleVec1d SelectStickTimeManager::NextThetas(double, const ForceParameters& fp, //'nexttime' unused.
                                               const DoubleVec1d& discounts) const
{
    DoubleVec1d newthetas;

    if (m_stair.empty())                // first riser being built
    {
        newthetas = fp.GetRegionalThetas();
        return newthetas;
    }

    stair::const_iterator prevjoint(m_stair.end());
    --prevjoint;
    newthetas = CalculateThetasWithPrevOnly(prevjoint,fp,discounts);

    return newthetas;

} // SelectStickTimeManager::NextThetas

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

double SelectStickTimeManager::Mean_delta_x(double x, double s, double mu,
                                            double nu, double delta_t) const
// compute M(delta_x) = delta_t(sx(1-x) + mu(1-x) - nu(x))
{
    double mean_delta(delta_t*(s*x*(1.0 - x) + mu*(1.0 - x) - nu*x));
    assert (-1.0 <= mean_delta && mean_delta <= 1.0);
    return mean_delta;
} // SelectStickTimeManager::Mean_delta_x

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

double SelectStickTimeManager::SD_delta_x(double x, double theta,
                                          double delta_t) const
{
    double var_delta(2.0*delta_t*x*(1.0 - x)/theta);
    assert(var_delta >= 0.0);
    return sqrt(var_delta);
} // SelectStickTimeManager::SD_delta_x

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

DoubleVec1d SelectStickTimeManager::CalculateThetasWithPrevAndNext(
    stair::const_iterator prevjoint, stair::const_iterator chosen,
    stair::const_iterator nextjoint, const ForceParameters& fp) const
{
    //MDEBUG If not all stick-joints are the same length, these formulae
    //may need to be weighted so that more distant points have less
    //influence than closer ones.

    double scoeff(fp.GetLogisticSelectionCoefficient()[0]);
    DoubleVec1d murates(fp.GetOnlyDiseaseRates());
    assert(murates.size() == 2);
    double toBigA(murates[0]), toSmallA(murates[1]);

    DoubleVec1d nextths(nextjoint->GetThetas());
    DoubleVec1d prevths(prevjoint->GetThetas());
    assert(nextths.size() == 2 && prevths.size() == 2);
    double nextfreq = nextths[0] / (nextths[0] + nextths[1]);
    double prevfreq = prevths[0] / (prevths[0] + prevths[1]);
    double delta_t1 = chosen->GetRootendTime() - chosen->GetTipendTime();
    double delta_t2 = prevjoint->GetRootendTime()- prevjoint->GetTipendTime();
    double mean_x =
        (nextfreq + Mean_delta_x(nextfreq,scoeff,toBigA,toSmallA,delta_t1) +
         prevfreq + Mean_delta_x(prevfreq,scoeff,toBigA,toSmallA,delta_t2))
        / 2.0;
    double sd_delta = (SD_delta_x(nextfreq, nextths[0]+nextths[1],delta_t1) +
                       SD_delta_x(prevfreq,prevths[0]+prevths[1],delta_t2)) / 2.0;

    return PickNewThetasWithMeanAndSD(mean_x,sd_delta,fp);

} // SelectStickTimeManager::CalculateThetasWithPrevAndNext

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

DoubleVec1d SelectStickTimeManager::CalculateThetasWithPrevOnly(
    stair::const_iterator prevjoint, const ForceParameters& fp) const
{
    double scoeff(fp.GetLogisticSelectionCoefficient()[0]);
    DoubleVec1d murates(fp.GetOnlyDiseaseRates());
    assert(murates.size() == 2);
    double toBigA(murates[0]), toSmallA(murates[1]);

    DoubleVec1d prevths(prevjoint->GetThetas());
    double prevfreq = prevths[0] / (prevths[0] + prevths[1]);
    double delta_t1 = prevjoint->GetRootendTime() - prevjoint->GetTipendTime();
    double mean_x = prevfreq + Mean_delta_x(prevfreq, scoeff, toBigA, toSmallA, delta_t1);
    double sd_delta = SD_delta_x(prevfreq, prevths[0] + prevths[1], delta_t1);

    return PickNewThetasWithMeanAndSD(mean_x,sd_delta,fp);

} // SelectStickTimeManager::CalculateThetasWithPrevOnly

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

DoubleVec1d SelectStickTimeManager::CalculateThetasWithPrevOnly(
    stair::const_iterator prevjoint, const ForceParameters& fp,
    const DoubleVec1d& discounts) const
{
    double scoeff(fp.GetLogisticSelectionCoefficient()[0]);
    DoubleVec1d murates(fp.GetOnlyDiseaseRates());
    assert(murates.size() == 2);
    double toBigA(murates[0]), toSmallA(murates[1]);

    DoubleVec1d prevths(prevjoint->GetThetas());
    double totaltheta = prevths[0] + prevths[1];
    double thetasq = totaltheta * totaltheta;
    double prevfreq = prevths[0] / totaltheta;
    double delta_t1 = prevjoint->GetRootendTime() - prevjoint->GetTipendTime();
    double mean_x = prevfreq + Mean_delta_x(prevfreq, scoeff, toBigA, toSmallA, delta_t1);
    mean_x += (discounts[0]*prevths[1]/prevths[0] -
               discounts[1]*prevths[0]/prevths[1]) * delta_t1 * delta_t1 * 2.0 / thetasq;
    double sd_delta = SD_delta_x(prevfreq, prevths[0] + prevths[1], delta_t1);

    return PickNewThetasWithMeanAndSD(mean_x,sd_delta,fp);

} // SelectStickTimeManager::CalculateThetasWithPrevOnly

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

DoubleVec1d SelectStickTimeManager::PickNewThetasWithMeanAndSD(
    double mean, double sd, const ForceParameters& fp) const
{
    DoubleVec1d newthetas(2, 0.0);
    double new_x;
    do {
        double pick = randomSource.Normal();
        new_x = pick * sd + mean;
    } while (new_x >= 1.0 || new_x <= 0.0);

    DoubleVec1d tipthetas(fp.GetRegionalThetas());
    double totaltheta(std::accumulate(tipthetas.begin(), tipthetas.end(), 0.0));
    // NB:  tipthetas are used here because they are handy, and we
    // assume that totaltheta is conserved throughout the tree.  Fix
    // if allowing totaltheta to change!
    newthetas[0] = new_x * totaltheta;
    newthetas[1] = (1.0 - new_x) * totaltheta;

    return newthetas;

} // SelectStickTimeManager::PickNewThetasWithMeanAndSD

//____________________________________________________________________________________
