// $Id: profile.cpp,v 1.74 2018/01/03 21:33:02 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 */


// profile.cpp [part of the analyzer module]
//
// - profiles (fixed and percentile)
//
// Peter Beerli November 2000
//

#include <cassert>
#include <iostream>
#include <vector>

#include "analyzer.h"
#include "constants.h"
#include "forcesummary.h"
#include "mathx.h"
#include "maximizer.h"
#include "parameter.h"
#include "registry.h"
#include "runreport.h"
#include "types.h"
#include "vector_constants.h"
#include "vectorx.h"

using namespace std;

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

const double PROFILE_EPSILON = 0.00001; // accuracy percentile in profiles

//------------------------------------------------------------------------------------
// returns a structure containing all possible profiletables
// according to ParamStruct *toDolist

void Analyzer::CalcProfiles (const DoubleVec1d MLEs, double likelihood, long int region)
{
    ParamVector toDolist(false);
    ParamVector::iterator i;
    long int ii;

    // setup and save the guides for the gradients
    m_MLEparams = MLEs;
    m_MLElparams = CreateVec1d(m_MLEparams.size(), static_cast<double>(0.0));
    LogVec0(m_MLEparams, m_MLElparams);
    m_likelihood = likelihood;

    // crank up runtime progress reports
    long int numprofiles = toDolist.NumProfiledParameters();
    RunReport& runreport = registry.GetRunReport();
    runreport.RecordProfileStart();
    long int thisprofile = 0;

    for (i = toDolist.begin (), //over all parameters in toDo list
             ii = 0; i != toDolist.end (); ++i, ++ii)
    {
        ParamStatus mystatus = i->GetStatus();
        ProfileStruct emptyprofile ;
        if (!mystatus.Valid()) continue;
        if (mystatus.Varies() && i->IsProfiled())
        {
            m_maximizer->ProfileGuideFix(ii);
            CalcProfile (i, ii, region);
            m_maximizer->ProfileGuideRestore();
            runreport.PrognoseProfiles(thisprofile, numprofiles);
            ++thisprofile;
        }
        else                          // constant or non-head grouped parameters
        {
            i->AddProfile(emptyprofile, m_maximizer->GetPostlikeTag());
        }
    }
}

//------------------------------------------------------------------------------------
// returns a structure containing a single profile table
// according to ParamStruct *toDolist

void Analyzer::CalcProfile (ParamVector::iterator guide, long int pnum, long int region)
{
    switch(guide->GetProfileType())
    {
        case profile_PERCENTILE :
            CalcProfilePercentile (guide, pnum, region);
            break;
        case profile_FIX :
            CalcProfileFixed (guide, pnum, region);
            break;
        case profile_NONE :
            assert(false); //This should have been taken care of in CalcProfiles.
            ProfileStruct emptyprofile ;
            guide->AddProfile(emptyprofile, m_maximizer->GetPostlikeTag());
            break;
    }
}

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

void Analyzer::CalcProfileFixed (ParamVector::iterator guide, long int pnum, long int region)
{
    ProfileStruct theprofile;
    m_newparams = m_MLEparams;

    vector < double >::const_iterator fix;
    unsigned long int i = 0u;
    DoubleVec1d modifiers = m_forcesummary.GetModifiers(pnum); // FS gets 'em from ParamVector

    for (fix = modifiers.begin(); fix != modifiers.end (); ++fix, ++i)
    {
        if (m_likelihood == -DBL_BIG)
        {
            AddBlankProfileForModifier(*fix, theprofile.profilelines);
            continue;
        }
        ProfileLineStruct profileline;
        double mlevalue = m_MLEparams[pnum];
        double newvalue = (*fix) * mlevalue;
        if (((guide->IsForce(force_GROW) && (i >= vecconst::growthmultipliers.size())) ||
             (guide->IsForce(force_LOGISTICSELECTION) &&
              (i >= vecconst::logisticselectionmultipliers.size()))) &&
            (registry.GetUserParameters().GetVerbosity() != CONCISE) &&
            (registry.GetUserParameters().GetVerbosity() != NONE   ))
        {
            newvalue = (*fix);
            // The second part of this vector is filled with values which we want
            // to *set* the param to, not to multiply the param by. --LS
        }
        m_newparams = m_MLEparams;
        bool islog = false;
        registry.GetForceSummary().SetParamWithConstraints(pnum, newvalue, m_newparams, islog);
        string message = "";
        bool retval(true);
        retval = m_maximizer->Calculate(m_newparams, profileline.loglikelihood, message);
        if (!retval)
        {
            if (profileline.loglikelihood != -DBL_BIG)
            {
                profileline.loglikelihood = -DBL_BIG;
                RunReport& runreport = registry.GetRunReport();
                string msg = "The maximizer failed for position ";
                msg += ToString(pnum) + ", without a corresponding catastrophically  "
                    + "low likelihood.  Re-setting to -" + Pretty(DBL_BIG)
                    + " and continuing.";
                runreport.ReportChat(msg);
            }
        }
        else if (message != "")
        {
            //We got a warning from the maximizer (not an error)
            message = "Warning:  When calculating the maximum for other parameters "
                "when " + guide->GetName() + " equals " + ToString(newvalue) +
                ", we received the following warning from the maximizer:  " +
                message;
            registry.GetRunReport().ReportDebug(message);
        }

        if (region != FLAGLONG)
        {
            //We need to convert the regional parameters to global parameters.
            ForceParameters fp(region);
            fp.SetRegionalParameters(m_newparams);
            m_newparams = fp.GetGlobalParameters();
        }
        profileline.profilevalue = m_newparams[pnum];
        profileline.profparam = m_newparams;
        profileline.percentile = (*fix);
        theprofile.profilelines.push_back(profileline);
    }

    guide->AddProfile(theprofile, m_maximizer->GetPostlikeTag());
}

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

void Analyzer::CalcProfilePercentile (ParamVector::iterator guide, long int pnum, long int region)
{
    ProfileStruct theprofile;
    vector<ProfileLineStruct> localprofiles;
    m_newparams = m_MLEparams;

    //Figure out what log likelihoods we need to hit.  These come from
    // the percentiles in m_forcesummary.
    DoubleVec1d percents = m_forcesummary.GetModifiers(pnum);
    DoubleVec1d lowTargetLikes;
    DoubleVec1d highTargetLikes;
    DoublesMap  percsForLowLikes, percsForHighLikes;
    for (unsigned long int percnum = 0; percnum<percents.size(); percnum++)
    {
        long int df = 1;
        // It's a bit weird, but when profiling, the degrees of freedom
        //  is 1, since that's the *difference* in degrees of freedom
        //  between letting all parameters vary and letting all but
        //  one parameter vary (he says, gesticulating wildly) --LS
        double percent = percents[percnum];
        if (percent < 0.5)
        {
            double targetLike = m_likelihood - (find_chi(df, (percent * 2.0))/2.0);
            if (!percsForLowLikes.insert(make_pair(targetLike, percent)).second)
            {
                //The new targetLike is the same as an old targetLike, perhaps because
                // the maximum likelihood is very very low.
                AddBlankProfileForModifier(percent, localprofiles);
            }
            lowTargetLikes.push_back(targetLike);
        }
        else
        {
            double targetLike = m_likelihood - (find_chi(df, (1.0 - percent)*2.0)/2.0);
            if (!percsForHighLikes.insert(make_pair(targetLike, percent)).second)
            {
                //The new targetLike is the same as an old targetLike, perhaps because
                // the maximum likelihood is very very low.
                AddBlankProfileForModifier(percent, localprofiles);
            }
            highTargetLikes.push_back(targetLike);
        }
    }

    // We now go through the low set first and the high set second.  We'll do
    // some setup first in each that's different, then the rest of the algorithm
    // needs to work for both types.
    DoHalfTheProfile(false, lowTargetLikes, percsForLowLikes,  pnum, region, localprofiles, guide);
    DoHalfTheProfile(true, highTargetLikes, percsForHighLikes, pnum, region, localprofiles, guide);
    theprofile.profilelines = localprofiles;
    guide->AddProfile(theprofile, m_maximizer->GetPostlikeTag());
}

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

void Analyzer::DoHalfTheProfile(bool high, DoubleVec1d& targetLikes,
                                DoublesMap& percsForLikes,
                                long int pnum, long int region,
                                vector<ProfileLineStruct>& localprofiles,
                                ParamVector::iterator guide)
{
    DoublesMap foundLikesForVals;
    DoubleToVecMap foundVecsForVals;
    DoubleToStringMap messagesForVals;
    string message;

    // ****Put the maximum into our likes and vectors.****
    foundLikesForVals.insert(make_pair(m_MLEparams[pnum], m_likelihood));
    foundVecsForVals.insert(make_pair(m_MLEparams[pnum], m_MLEparams));
    messagesForVals.insert(make_pair(m_MLEparams[pnum], ""));

    // ****Put one extreme value into our likes and vectors.****
    double extremeParam, extremeLike;
    if (high == false)
    {
        extremeParam = LowParameter(pnum, m_MLEparams[pnum]);
    }
    else
    {
        extremeParam = HighParameter(pnum, m_MLEparams[pnum]);
    }
    m_newparams = m_MLEparams;
    bool islog = false;
    registry.GetForceSummary().SetParamWithConstraints(pnum, extremeParam, m_newparams, islog);

    m_maximizer->Calculate(m_newparams, extremeLike, message);

    //Note:  maximizer failure is fine--it's supposed to be extreme.
    foundLikesForVals.insert(make_pair(extremeParam, extremeLike));
    foundVecsForVals.insert(make_pair(extremeParam, m_newparams));
    messagesForVals.insert(make_pair(extremeParam, message));

    // ****Now we can start looking for our targets, starting from the closest to the MLE.****
    sort(targetLikes.begin(), targetLikes.end());
    reverse(targetLikes.begin(), targetLikes.end());

    for (DoubleVec1d::iterator targetLike = targetLikes.begin();
         targetLike != targetLikes.end(); targetLike++)
    {
        ProfileLineStruct localprofile;
        double percent = percsForLikes.find(*targetLike)->second;
        if (m_likelihood == -DBL_BIG)
        {
            AddBlankProfileForModifier(percent, localprofiles);
            continue;
        }
        DoublesMapiter highValAndLike = GetLastHigher(foundLikesForVals, *targetLike, high);
        DoublesMapiter lowValAndLike =  GetFirstLower(foundLikesForVals, *targetLike, high);
        DoublesMapiter closestFoundValAndLike = ClosestFoundFor(foundLikesForVals, *targetLike);

        bool keep_going = true;
        while ((fabs(closestFoundValAndLike->second - *targetLike) > PROFILE_EPSILON) && keep_going)
        {
            if (lowValAndLike == foundLikesForVals.end())
            {
                keep_going = AddMoreExtremeValue(foundLikesForVals, foundVecsForVals,
                                                 messagesForVals, high, pnum);
                highValAndLike = GetLastHigher(foundLikesForVals, *targetLike, high);
                lowValAndLike = GetFirstLower(foundLikesForVals, *targetLike, high);
                closestFoundValAndLike = ClosestFoundFor(foundLikesForVals, *targetLike);
                continue;
            }
            if (fabs((highValAndLike->first - lowValAndLike->first)
                     / max(fabs(highValAndLike->first), fabs(lowValAndLike->first)))
                > PROFILE_EPSILON)
            {
                m_newparams = (foundVecsForVals.find(highValAndLike->first))->second;
                double newlike;
                double newval = GetNewValFromBracket(highValAndLike, lowValAndLike, *targetLike);
                registry.GetForceSummary().SetParamWithConstraints(pnum, newval, m_newparams,islog);
                m_maximizer->Calculate(m_newparams, newlike, message);
                //Again, we're not worrying about maximization failure here--we'll
                // assume we're simply scootching in from the most extreme value so far.
                foundLikesForVals.insert(make_pair(newval, newlike));
                foundVecsForVals.insert(make_pair(newval, m_newparams));
                messagesForVals.insert(make_pair(newval, message));
                if (newlike < *targetLike)
                {
                    lowValAndLike = foundLikesForVals.find(newval);
                }
                else
                {
                    highValAndLike = foundLikesForVals.find(newval);
                }
            }
            else
            {
                //The likelihoods are not near each other, but the absolute values
                // of the parameters are.  The most likely cause of this situation
                // is that the lower likelihood is wrong because the maximizer didn't
                // have very good starting values when it tried it.  So, we re-try
                // that lower value and see if we get a better likelihood.
                keep_going = ExpandSearch(foundLikesForVals, foundVecsForVals,
                                          lowValAndLike, highValAndLike,
                                          messagesForVals, pnum, *targetLike);
                highValAndLike = GetLastHigher(foundLikesForVals, *targetLike, high);
                lowValAndLike = GetFirstLower(foundLikesForVals, *targetLike, high);
            }
            closestFoundValAndLike =
                ClosestFoundFor(foundLikesForVals, *targetLike);
        }

        // ****We're as close as we're going to get to our target--add it.****
        if (region != FLAGLONG)
        {
            //We need to convert the regional parameters to global parameters.
            ForceParameters fp(region);
            fp.SetRegionalParameters(m_newparams);
            m_newparams = fp.GetGlobalParameters();
        }
        double foundVal = closestFoundValAndLike->first;
        double foundLike = closestFoundValAndLike->second;
        if (lowValAndLike == foundLikesForVals.end())
        {
            //All likelihoods are greater than our target likelihood.  Take the
            // most extreme value and use that.  However, label the profile
            // as being extreme so we can put a '<' or '>' in front of it for
            // the output report.
            if (high)
            {
                foundVal = foundLikesForVals.rbegin()->first;
                foundLike = foundLikesForVals.rbegin()->second;
                localprofile.isExtremeHigh = true;
            }
            else
            {
                foundVal = foundLikesForVals.begin()->first;
                foundLike = foundLikesForVals.begin()->second;
                localprofile.isExtremeLow = true;
            }
        }

        string warning = messagesForVals.find(foundVal)->second;
        if (warning != "")
        {
            //We got a warning from the maximizer (not an error)
            string msg = "Warning:  When calculating the maximum for other parameters ";
            msg+= "for the " + ToString(percent) + " profile for "
                + guide->GetName() +
                " (when it equals " + ToString(foundVal) +
                "), we received the following warning from the maximizer:  " +
                warning;
            registry.GetRunReport().ReportDebug(msg);
            localprofile.maximizerWarning = true;
        }

        localprofile.profilevalue = foundVal;
        localprofile.loglikelihood = foundLike;
        localprofile.profparam = foundVecsForVals.find(foundVal)->second;
        localprofile.percentile = percent;

        if (region != FLAGLONG)
        {
            //We might need to convert from regional values to global values
            ForceParameters fp(region);
            fp.SetRegionalParameters(localprofile.profparam);
            localprofile.profparam = fp.GetGlobalParameters();
            localprofile.profilevalue = (fp.GetGlobalParameters())[pnum];
        }

        localprofiles.push_back(localprofile);
    }

    CheckForMultipleMaxima(foundLikesForVals, high);
}

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

bool Analyzer::AddMoreExtremeValue(DoublesMap& foundLikesForVals,
                                   DoubleToVecMap& foundVecsForVals,
                                   DoubleToStringMap& messagesForVals,
                                   bool high, long int pnum)
{
    double oldextreme;
    if (high)
    {
        oldextreme = foundLikesForVals.rbegin()->first;
    }
    else
    {
        oldextreme = foundLikesForVals.begin()->first;
    }
    double newextreme;
    if (high)
    {
        newextreme = HighParameter(pnum, oldextreme);
        if (newextreme <= oldextreme)
        {
            return false;
        }
    }
    else
    {
        newextreme = LowParameter(pnum, oldextreme);
        if (newextreme >= oldextreme)
        {
            return false;
        }
    }
    m_newparams = foundVecsForVals.find(oldextreme)->second;
    bool islog = false;
    registry.GetForceSummary().SetParamWithConstraints(pnum, newextreme, m_newparams, islog);
    double foundLike;
    string message;
    m_maximizer->Calculate(m_newparams, foundLike, message);
    //We don't care if this fails.
    foundLikesForVals.insert(make_pair(newextreme, foundLike));
    foundVecsForVals.insert(make_pair(newextreme, m_newparams));
    messagesForVals.insert(make_pair(newextreme, message));
    return true;
} // FillMapsWithExtremes

//------------------------------------------------------------------------------------
//ExpandSearch checks to see if re-analyzing the value with the lower
// likelihood with the starting values from the higher likelihood results
// in a higher likelihood.  If so,

bool Analyzer::ExpandSearch(DoublesMap& foundLikesForVals,
                            DoubleToVecMap& foundVecsForVals,
                            DoublesMapiter& lowValAndLike,
                            DoublesMapiter& highValAndLike,
                            DoubleToStringMap& messagesForVals,
                            long int pnum, double targetLike)
{
    //First, find out which DoublesMapiter contains the greater likelihood.
    DoublesMapiter highLike, lowLike;
    if (lowValAndLike->second > highValAndLike->second)
    {
        assert(false); //I *think* this should never happen. Sadly, my brain is full.
        highLike = lowValAndLike;
        lowLike = highValAndLike;
    }
    else
    {
        highLike = highValAndLike;
        lowLike = lowValAndLike;
    }
    m_newparams = (foundVecsForVals.find(highLike->first))->second;
    double newvalue = (foundVecsForVals.find(lowLike->first))->second[pnum];
    bool islog = false;
    registry.GetForceSummary().SetParamWithConstraints(pnum, newvalue, m_newparams,islog);
    double newlike;
    string message;
    bool retval(true);
    retval = m_maximizer->Calculate(m_newparams, newlike, message);
    if (!retval)
        return false;
    if (newlike <= targetLike)
    {
        return false;
    }
    //Replace the old set of parameters with the new one.
    double oldval = lowLike->first;
    foundLikesForVals.erase(oldval);
    foundVecsForVals.erase(oldval);
    messagesForVals.erase(oldval);
    foundLikesForVals.insert(make_pair(newvalue, newlike));
    foundVecsForVals.insert(make_pair(newvalue, m_newparams));
    messagesForVals.insert(make_pair(newvalue, message));

    return true;
}

//------------------------------------------------------------------------------------
// returns high/highest and low/lowest values for specific forces

double Analyzer::LowParameter(long int pnum, double currentLow)
{
    double value = m_forcesummary.GetLowParameter(pnum);
    if (value > m_MLEparams[pnum])
    {
        if (m_MLEparams[pnum] > 0)
        {
            //Take the next-lowest power of 10 from the MLE.
            int exponent = static_cast<int>(log10(m_MLEparams[pnum]));
            value = pow(10.0,exponent-1);
        }
        else if (m_MLEparams[pnum] < 0)
        {
            //Take the next-highest power of 10 from the MLE, but negative.
            int exponent = static_cast<int>(log10(fabs(m_MLEparams[pnum])));
            value = -pow(10.0,exponent+1);
        }
        else
        {
            //m_MLEparams[pnum] == 0, presumably in a case where going negative
            // is fatal.  There can be no profiling curve below zero, so
            // just set this to 0 and go on.
            value = 0;
            static bool firsttime = true;
            if (firsttime)
            {
                RunReport& runreport = registry.GetRunReport();
                runreport.ReportChat("The MLE for a parameter was zero, which means no profiling can be performed"
                                     " below that value.  Normal profiles should result from values"
                                     " greater than the parameter.");
            }
            firsttime = false;
        }
    }
    for (long int nmult = 0; nmult < 5; nmult++)
    {
        if (value >= currentLow-EPSILON)
        {
            //The EPSILON is a fudge factor for optimized code.
            value *= m_forcesummary.GetLowMult(pnum);
        }
        else
        {
            return value;
        }
    }
    return value;
}

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

double Analyzer::HighParameter(long int pnum, double currentHigh)
{
    double value = m_forcesummary.GetHighParameter(pnum);
    if (value < m_MLEparams[pnum])
    {
        if (value > 0)
        {
            //Take the next-highest power of 10 from the MLE
            int exponent = static_cast<int>(log10(m_MLEparams[pnum]));
            value = pow(10.0,exponent+1);
        }
        else
        {
            //What the heck are we doing with a negative 'high parameter'??
            assert(false);
            value = 10;
        }
    }
    for (long int nmult = 0; nmult < 5; nmult++)
    {
        if (value <= currentHigh+EPSILON)
        {
            //The EPSILON is a fudge factor for optimized code.
            value *= m_forcesummary.GetHighMult(pnum);
        }
        else
        {
            return value;
        }
    }
    return value;
}

//------------------------------------------------------------------------------------
//GetLastHigher returns an iterator to the 'last' likelihood with a
// higher likelihood than the target likelihood.  'Last' means 'The last going
// away from the MLE', so if we're profiling the upper half of the profile,
// this means we need to use a reverse iterator (since the map is stored
// in the order of the values, not the likelihoods).  There should always be
// at least one entry in the map with a higher likelihood, which means
// that if there are no entries in the map with a lower likelihood, we return
// an iterator to the most extreme value (.begin() in the case of the upper
// half of the profile).

DoublesMapiter Analyzer::GetLastHigher(DoublesMap& foundLikesForVals, double targetLike, bool high)
{
    DoublesMapiter upwardsLike = foundLikesForVals.begin();
    DoublesMapReviter backwardsLike = foundLikesForVals.rbegin();
    for (; upwardsLike != foundLikesForVals.end();
         upwardsLike++, backwardsLike++)
    {
        if (high)
        {
            if (upwardsLike->second < targetLike)
            {
                upwardsLike--;
                return upwardsLike;
            }
        }
        else
        {
            if (backwardsLike->second < targetLike)
            {
                backwardsLike--;
                return foundLikesForVals.find(backwardsLike->first);
            }
        }
    }
    //Nothing was lower--return the last value
    if (high)
    {
        upwardsLike = foundLikesForVals.end();
        upwardsLike--;
        return upwardsLike;
    }
    else
    {
        return foundLikesForVals.begin();
    }
}

//------------------------------------------------------------------------------------
//GetFirstLower returns an iterator to the 'first' likelihood with a lower
// likelihood than the target likelihood.  'First' means 'the first found
// going away from the MLE'.  If we are profiling the lower half (high=false),
// this means we have to use the reverse iterator.
//
// There will not always be a value with a lower likelihood than the
//  target--in this case, return the .end() iterator, regardless of which
//  direction we're going.

DoublesMapiter Analyzer::GetFirstLower(DoublesMap& foundLikesForVals, double targetLike, bool high)
{
    DoublesMapiter upwardsLike = foundLikesForVals.begin();
    DoublesMapReviter backwardsLike = foundLikesForVals.rbegin();
    for (; upwardsLike != foundLikesForVals.end();
         upwardsLike++, backwardsLike++)
    {
        if (high)
        {
            if (upwardsLike->second < targetLike)
            {
                return upwardsLike;
            }
        }
        else
        {
            if (backwardsLike->second < targetLike)
            {
                return foundLikesForVals.find(backwardsLike->first);
            }
        }
    }
    return foundLikesForVals.end();
}

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

DoublesMapiter Analyzer::ClosestFoundFor(DoublesMap& foundLikesForVals, double targetLike)
{
    DoublesMapiter retval = foundLikesForVals.begin();
    for (DoublesMapiter valLike = foundLikesForVals.begin(); valLike != foundLikesForVals.end(); valLike++)
    {
        if (fabs(valLike->second - targetLike) < fabs(retval->second  - targetLike))
        {
            retval = valLike;
        }
    }
    return retval;
}

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

double Analyzer::GetNewValFromBracket(DoublesMapiter& highValAndLike, DoublesMapiter& lowValAndLike,
                                      double targetLike)
{
    //LS NOTE:  I'm changing this to just return halfway between the y
    // values instead of trying to be clever about it.  There were too many
    // edge cases where being clever took way longer.
    double y1 = lowValAndLike->first;
    double y2 = highValAndLike->first;
    return (y1+y2)/2;

    //For now, this will return a linear regression of the point in question.
    // We might want to query the maximizer for the gradient at the high and
    // low points for a better estimate.

    //The formula we use is (y-y1) = m(x-x1), with m=(x2-x1)/(y2-y1)
#if 0
    return (( (highValAndLike->second - lowValAndLike->second) /
              (highValAndLike->first  - lowValAndLike->first) )
            * (targetLike - lowValAndLike->second)) + lowValAndLike->first;
#endif
#if 0
    double x1 = lowValAndLike->second;
    double x2 = highValAndLike->second;
    double y1 = lowValAndLike->first;
    double y2 = highValAndLike->first;
    double x = targetLike;
    if (x2 == -DBL_BIG || x1 == -DBL_BIG)
    {
        return (y1+y2)/2;
    }
    double retval = (( (y2-y1)/(x2-x1) ) * (x-x1) ) + y1;
    assert ((x1 < x && x < x2) ||
            (x1 > x && x > x2));
    assert ((y1 < retval && retval < y2) ||
            (y1 > retval && retval > y2));
    return retval;
#endif
}

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

void Analyzer::AddBlankProfileForModifier(double modifier, vector<ProfileLineStruct>& localprofiles)
{
    ProfileLineStruct blankprofile;
    blankprofile.isExtremeLow = true;
    blankprofile.isExtremeHigh = true;
    blankprofile.profilevalue = 0.0;
    blankprofile.loglikelihood = -DBL_BIG;
    blankprofile.profparam = m_MLEparams;
    blankprofile.percentile = modifier;
    localprofiles.push_back(blankprofile);
}

//------------------------------------------------------------------------------------
//We don't do anything with this call currently except print out a debug
// message if it returns true.  We're fairly late in the process to try to go
// back and fix the MLE (if indeed we need to do that).

void Analyzer::CheckForMultipleMaxima(DoublesMap& valLikeMap, bool high)
{
    bool foundmultiples = false;
    double oldlike = valLikeMap.begin()->second;
    for (DoublesMapiter valLike = valLikeMap.begin(); valLike != valLikeMap.end(); valLike++)
    {
        double newlike = valLike->second;
        if (high)
        {
            if (newlike > oldlike)
            {
                foundmultiples = true;
            }
        }
        else
        {
            if (newlike < oldlike)
            {
                foundmultiples = true;
            }
        }
    }
    if (foundmultiples)
    {
        string msg = "Multiple maxima?  A better maximum?  These likelihoods should ";
        msg += (high ? "decrease" : "increase" );
        msg += " as we go.\nParamVal\tLikelihood\n";
        registry.GetRunReport().ReportDebug(msg);
#ifndef NDEBUG
        PrintDoublesMap(valLikeMap);
#endif
    }
}

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

void Analyzer::PrintDoublesMap(DoublesMap& printme)
{
    for (DoublesMapiter pmap = printme.begin(); pmap != printme.end(); pmap++)
    {
        PrintDoublesIter(pmap);
    }
}

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

void Analyzer::PrintDoublesIter(DoublesMapiter& printme)
{
    cout << Pretty(printme->first, 16) << "\t" << Pretty(printme->second, 16)
         << endl;
}

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

void Analyzer::PrintDoubleToStringMap(DoubleToStringMap& printme)
{
    for (DoubleToStringiter dsiter = printme.begin(); dsiter != printme.end(); dsiter++)
    {
        PrintDoubleToStringIter(dsiter);
    }
}

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

void Analyzer::PrintDoubleToStringIter(DoubleToStringiter& printme)
{
    cout << Pretty(printme->first, 16) << "\t" << Pretty(printme->second, 16)
         << endl;
}

//____________________________________________________________________________________
