// $Id: bayesanalyzer_1d.cpp,v 1.40 2018/01/03 21:32:54 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Lucian Smith, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


// Devised May 2004 by Lucian Smith
// Uses data from a CollectionManager to do a bayesian analysis.

#include <cassert>
#include <fstream>
#include <iostream>

#include "bayesanalyzer_1d.h"
#include "bayescurve.h"
#include "bayesparamlike_1d.h"
#include "collector.h"
#include "collmanager.h"
#include "constants.h"
#include "force.h"
#include "forcesummary.h"
#include "mathx.h"
#include "parameter.h"
#include "registry.h"
#include "runreport.h"
#include "userparam.h"
#include "vector_constants.h"
#include "paramstat.h"

using namespace std;

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

BayesAnalyzer_1D::BayesAnalyzer_1D()
    : m_currentChain(0), m_currentReplicate(0), m_currentRegion(0),
      m_forcesummary(registry.GetForceSummary())
{
    const ParamVector paramvec(true);
    for (unsigned long int i = 0; i < paramvec.size(); i++)
    {
        BayesParamLike_1D bp(paramvec[i]);
        m_blankBayesParamVec.push_back(bp);
        m_pstats.push_back(paramvec[i].GetStatus());
    }
    m_startparams = registry.GetForceSummary().GetStartParameters().GetGlobalParameters();
}

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

BayesAnalyzer_1D::~BayesAnalyzer_1D()
{
    // intentionally blank
}

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

void BayesAnalyzer_1D::AnalyzeAndAdd(const ParamSumm paramsumm)
{
    if (m_currentChain == 0)
    {
        m_paramlikes.clear();
    }

    //Make new BayesParamLikes by force, stick the data in 'em, then
    // run the curve smoothing algorithms on a per-chain basis.
    vector<BayesParamLike_1D> bayesparamls = m_blankBayesParamVec;
    for (unsigned long int sample = 0; sample < paramsumm.size(); sample++)
    {
        DoubleVec1d parameters = paramsumm[sample].first.GetGlobalParameters();
        long int freq          = paramsumm[sample].second;
        for (unsigned long int pnum = 0; pnum < parameters.size(); pnum++)
        {
            if (IsVariable(pnum))
            {
                bayesparamls[pnum].AddPoint(parameters[pnum], freq);
            }
        }
    }

    //All the data has been added; now smooth the curve.
    for (unsigned long int pnum = 0; pnum<bayesparamls.size(); pnum++)
    {
        if (IsVariable(pnum))
        {
            bayesparamls[pnum].SmoothCurve();
        }
    }

    //And now stick it into the appropriate member variables.
    m_paramlikes.push_back(bayesparamls);
    m_currentChain++;

}

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

//You might call the Replace routine if you do a run with more than one
// chain, wanted to analyze the first chain, but didn't want to keep the
// results.

void BayesAnalyzer_1D::ReplaceLastChainAndAnalyze(const ParamSumm paramsumm)
{
    if (m_currentChain > 0)
    {
        m_currentChain--;
        m_paramlikes.pop_back();
    }
    AnalyzeAndAdd(paramsumm);
}

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

void BayesAnalyzer_1D::EndChainsAndAnalyze()
{
    assert (m_paramlikes.size() > 0);

    vector < vector < BayesCurve > > curvesbyparam;
    //Dimensions are [parameter][chain], *not* the other way around.
    // We want it this way so we can sum each parameter over its chains.

    //Set up the curvesbyparam vector.
    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        vector < BayesCurve> onecurve;
        onecurve.push_back(m_paramlikes[0][param].GetSmoothedCurve());
        curvesbyparam.push_back(onecurve);
    }

    //And now add the other chains to the vectors.
    //LS NOTE:  our current analysis discards previous chains, so this never
    // gets used.
    for (long int chain = 1; chain < m_currentChain; chain++)
    {
        for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
            curvesbyparam[param].push_back(m_paramlikes[chain][param].GetSmoothedCurve());
    }

    vector <BayesCurve> summedcurves;
    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        BayesCurve summedcurve(curvesbyparam[param], ADD);
        summedcurves.push_back(summedcurve);
    }

    if (m_currentReplicate == 0)
    {
        vector < vector <BayesCurve> > sumcurvevec;
        sumcurvevec.push_back(summedcurves);
        m_replicatecurves.push_back(sumcurvevec);
    }
    else
        m_replicatecurves[m_currentRegion].push_back(summedcurves);

    //m_paramlikes.clear();
    m_currentReplicate++;
    m_currentChain = 0;
}

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

void BayesAnalyzer_1D::EndReplicatesAndAnalyze()
{
    vector < vector < BayesCurve > > curvesbyparam;
    //Dimensions are [parameter][replicate], *not* the other way around.
    // We want it this way so we can sum each parameter over its replicates.

    //Set up the curvesbyparam vector.
    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        vector < BayesCurve> onecurve;
        onecurve.push_back(m_replicatecurves[m_currentRegion][0][param]);
        curvesbyparam.push_back(onecurve);
    }

    //And now add the other replicates to the vectors.
    for (long int replicate = 1; replicate < m_currentReplicate; replicate++)
    {
        for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
            curvesbyparam[param].push_back(m_replicatecurves[m_currentRegion][replicate][param]);
    }

    vector <BayesCurve> summedcurves;
    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        BayesCurve summedcurve(curvesbyparam[param], ADD);
        summedcurves.push_back(summedcurve);
    }

    m_regioncurves.push_back(summedcurves);

    m_currentRegion++;
    m_currentReplicate = 0;
    m_currentChain = 0;
}

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

void BayesAnalyzer_1D::EndRegionsAndAnalyze()
{
    //Basically the same as EndReplicates, except we multiply the curves instead
    // of adding them.

    assert (m_regioncurves.size() > 0);
    vector < vector < BayesCurve > > curvesbyparam;
    //Dimensions are [parameter][region], *not* the other way around.
    // We want it this way so we can sum each parameter over its replicates.

    //Set up the curvesbyparam vector.
    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        vector < BayesCurve> onecurve;
        onecurve.push_back(m_regioncurves[0][param]);
        curvesbyparam.push_back(onecurve);
    }

    //And now add the other regions to the vectors.
    for (long int region = 1; region < m_currentRegion; region++)
    {
        for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
            curvesbyparam[param].push_back(m_regioncurves[region][param]);
    }

    for (unsigned long int param = 0; param < m_blankBayesParamVec.size(); param++)
    {
        BayesCurve summedcurve(curvesbyparam[param], MULTIPLY);
        m_allregioncurves.push_back(summedcurve);
    }
}

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

//The chain curves are stored in the vector of BayesParamLikes, while
// the region and overall curves are stored as member variables.

DoubleVec1d BayesAnalyzer_1D::GetMaxVecForLastChain()
{
    DoubleVec1d results = m_startparams;
    unsigned long int chain = m_paramlikes.size()-1;

    for (unsigned long int parameter = 0; parameter < m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            bool islog(false);
            double newval = GetMaxForChain(chain, parameter);
            registry.GetForceSummary().SetParamWithConstraints(parameter, newval, results, islog);
        }
    }
    return results;
}

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

double BayesAnalyzer_1D::GetAvgMaxLikeForLastChain()
{
    double result = 0.0;
    long int pcount = 0;
    unsigned long int chain = m_paramlikes.size()-1;

    for (unsigned long int parameter = 0; parameter<m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            result += GetLikeAtMaxForChain(chain, parameter);
            pcount++;
        }
    }

    if (pcount>0)
        return result/pcount;
    else return 1.0;
}

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

double BayesAnalyzer_1D::GetAvgMaxLikeForRegion(long int region)
{
    double result = 0.0;
    long int pcount = 0;

    for (unsigned long int parameter = 0; parameter < m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            result += GetLikeAtMaxForRegion(region, parameter);
            pcount++;
        }
    }

    if (pcount>0)
        return result/pcount;
    else return 1.0;
}

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

double BayesAnalyzer_1D::GetAvgMaxLikeForAllRegions()
{
    double result = 0.0;
    long int pcount = 0;

    for (unsigned long int parameter = 0; parameter < m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            result += GetLikeAtMaxForAllRegions(parameter);
            pcount++;
        }
    }

    if (pcount>0)
        return result/pcount;
    else return 1.0;
}

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

double BayesAnalyzer_1D::GetMaxForChain(long int chain, long int parameter)
{
    if (IsVariable(parameter))
        return m_paramlikes[chain][parameter].GetSmoothedCurve().GetMax();
    else return m_startparams[parameter];
}

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

double BayesAnalyzer_1D::GetLikeAtMaxForChain(long int chain, long int parameter)
{
    if (IsVariable(parameter))
        return m_paramlikes[chain][parameter].GetSmoothedCurve().GetLikeAtMax();
    else return 1.0;
}

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

double BayesAnalyzer_1D::GetMaxForRegion(long int region, long int parameter)
{
    if (IsVariable(parameter))
        return m_regioncurves[region][parameter].GetMax();
    else return m_startparams[parameter];
}

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

double BayesAnalyzer_1D::GetLikeAtMaxForRegion(long int region, long int parameter)
{
    if (IsVariable(parameter))
        return m_regioncurves[region][parameter].GetLikeAtMax();
    else return 1.0;
}

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

DoubleVec1d BayesAnalyzer_1D::GetMaxVecForRegion(long int region)
{
    DoubleVec1d results = m_startparams;
    bool islog = false;

    for (unsigned long int parameter = 0; parameter < m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            double newval = GetMaxForRegion(region, parameter);
            registry.GetForceSummary().SetParamWithConstraints(parameter, newval, results, islog);
        }
    }

    return results;
}

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

double BayesAnalyzer_1D::GetPercentileAtValForRegion(double val, long int region, long int parameter)
{
    if (IsVariable(parameter))
        return m_regioncurves[region][parameter].GetPercentileAtVal(val);
    else return 0.5;
}

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

double BayesAnalyzer_1D::GetValAtPercentileForRegion(double val, long int region, long int parameter)
{
    if (IsVariable(parameter))
        return m_regioncurves[region][parameter].GetValAtPercentile(val);
    else return m_startparams[parameter];
}

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

double BayesAnalyzer_1D::GetLikeAtValForRegion(double val, long int region, long int parameter)
{
    if (IsVariable(parameter))
        return m_regioncurves[region][parameter].GetLikeAtVal(val);
    else return 0.0;
}

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

double BayesAnalyzer_1D::GetLikeAtValForReplicate(double val, long int region, long int replicate, long int parameter)
{
    if (IsVariable(parameter))
        return m_replicatecurves[region][replicate][parameter].GetLikeAtVal(val);
    else return 0.0;
}

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

double BayesAnalyzer_1D::GetMaxForAllRegions(long int parameter)
{
    if (IsVariable(parameter))
        return m_allregioncurves[parameter].GetMax();
    else return m_startparams[parameter];
}

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

double BayesAnalyzer_1D::GetLikeAtMaxForAllRegions(long int parameter)
{
    if (IsVariable(parameter))
        return m_allregioncurves[parameter].GetLikeAtMax();
    else return 1.0;
}

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

DoubleVec1d BayesAnalyzer_1D::GetMaxVecForAllRegions()
{
    DoubleVec1d results = m_startparams;
    bool islog(false);

    for (unsigned long int parameter = 0; parameter < m_startparams.size(); parameter++)
    {
        if (IsVariable(parameter))
        {
            double newval = GetMaxForAllRegions(parameter);
            registry.GetForceSummary().SetParamWithConstraints(parameter, newval, results, islog);
        }
    }

    return results;
}

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

double BayesAnalyzer_1D::GetValAtPercentileForAllRegions(double percentile, long int parameter)
{
    if (IsVariable(parameter))
        return m_allregioncurves[parameter].GetValAtPercentile(percentile);
    else return m_startparams[parameter];
}

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

double BayesAnalyzer_1D::GetPercentileAtValForAllRegions(double val, long int parameter)
{
    if (IsVariable(parameter))
        return m_allregioncurves[parameter].GetPercentileAtVal(val);
    else return 0.5;
}

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

double BayesAnalyzer_1D::GetLikeAtValForAllRegions(double val, long int parameter)
{
    if (IsVariable(parameter))
        return m_allregioncurves[parameter].GetLikeAtVal(val);
    else return 0.0;
}

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

bool BayesAnalyzer_1D::IsVariable(unsigned long int parameter)
{
    return m_pstats[parameter].Inferred();
}

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

void BayesAnalyzer_1D::CalcProfiles(long int region)
{
    if (!((region == FLAGLONG) && (m_regioncurves.size() == 1)))
        registry.GetRunReport().RecordProfileStart();

    ParamVector toDolist(false);
    ParamVector::iterator param;
    long int paramnum;

    for (param = toDolist.begin(), paramnum = 0; param != toDolist.end(); ++param, ++paramnum)
    {
        ProfileStruct emptyprofile ;
        if (!m_pstats[paramnum].Valid()) continue;
        if (!m_pstats[paramnum].Inferred())
        {
            if (region==FLAGLONG)
                param->AddProfile(emptyprofile, ltype_region);
            else
                param->AddProfile(emptyprofile, ltype_replicate);
        }
        else
            CalcProfile (param, paramnum, region);
    }
}

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

void BayesAnalyzer_1D::CalcProfile(ParamVector::iterator param, long int nparam, long int nregion)
{
    likelihoodtype ltype;
    double MLE, MLElike, MLEperc;

    if (nregion == FLAGLONG)
    {
        ltype = ltype_region; //'region' is an enum.
        MLE = GetMaxForAllRegions(nparam);
        MLElike = GetLikeAtMaxForAllRegions(nparam);
        MLEperc = GetPercentileAtValForAllRegions(MLE, nparam);
    }
    else
    {
        ltype = ltype_replicate; //Could be anything as long as it's not 'region'.
        MLE = GetMaxForRegion(nregion, nparam);
        MLElike = GetLikeAtMaxForRegion(nregion, nparam);
        MLEperc = GetPercentileAtValForRegion(MLE, nregion, nparam);
    }
    proftype ptype = param->GetProfileType();
    if (ptype == profile_NONE)
    {
        assert(false);    // Profiling should always be on for bayesian parameters.
        ProfileStruct emptyprofile ;
        param->AddProfile(emptyprofile, ltype);
        return;
    }

    DoubleVec1d modifiers = m_forcesummary.GetModifiers(nparam);

    ProfileStruct theprofile;

    for (unsigned long int i = 0u; i < modifiers.size(); ++i)
    {
        ProfileLineStruct profileline;
        double perc, val, likelihood;

        if (ptype == profile_FIX)
        {
            if(param->IsForce(force_GROW) &&
               (registry.GetUserParameters().GetVerbosity() != CONCISE) &&
               (registry.GetUserParameters().GetVerbosity() != NONE   ))
            {
                if (i < vecconst::growthmultipliers.size())
                    val = modifiers[i] * MLE;
                else
                    val = modifiers[i];
                // The second part of this vector is filled with values which we want
                // to *set* growth to, not to multiply growth by. --LS
            }
            else if(param->IsForce(force_LOGISTICSELECTION) &&
                    (registry.GetUserParameters().GetVerbosity() != CONCISE) &&
                    (registry.GetUserParameters().GetVerbosity() != NONE   ))
            {
                if (i < vecconst::logisticselectionmultipliers.size())
                    val = modifiers[i] * MLE;
                else
                    val = modifiers[i];
                // The second part of this vector is filled with values which we want
                // to *set* the logistic selection coeff. to, not to multiply it by. --LS
            }
            else
                val = modifiers[i] * MLE;
            if (ltype == ltype_region)
            {
                perc = GetPercentileAtValForAllRegions(val, nparam);
                likelihood = GetLikeAtValForAllRegions(val, nparam);
            }
            else
            {
                perc = GetPercentileAtValForRegion(val, nregion, nparam);
                likelihood = GetLikeAtValForRegion(val, nregion, nparam);
            }
        }
        else                            //ptype == percentile;
        {
            perc = modifiers[i];
            if (ltype == ltype_region)
            {
                val = GetValAtPercentileForAllRegions(perc, nparam);
                likelihood = GetLikeAtValForAllRegions(val, nparam);
            }
            else
            {
                val = GetValAtPercentileForRegion(perc, nregion, nparam);
                likelihood = GetLikeAtValForRegion(val, nregion, nparam);
            }
        }
        profileline.percentile = perc;
        profileline.profilevalue = val;
        profileline.loglikelihood = likelihood; //Not really a *log*, but anyway.
        theprofile.profilelines.push_back(profileline);
        //LS TEST
        // cout << perc << ", " << val << ", " << likelihood << endl;
    }

    ProfileLineStruct profileline;

    profileline.percentile = MLEperc;
    profileline.profilevalue = MLE;
    profileline.loglikelihood = MLElike;
    theprofile.profilelines.push_back(profileline);
    //LS TEST
    // cout << MLEperc << ", " << MLE << ", " << MLElike << endl;

    param->AddProfile(theprofile, ltype);
} // BayesAnalyzer_1D::CalcProfile

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

vector <long int> BayesAnalyzer_1D::GetNumUniquePointsVec()
{
    vector <long int> results;
    unsigned long int chain = m_paramlikes.size()-1;
    for (unsigned long int pnum = 0; pnum < m_paramlikes[chain].size(); pnum++)
        results.push_back(m_paramlikes[chain][pnum].GetNumUniquePoints());
    return results;
}

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

double BayesAnalyzer_1D::GetMinParamValFromCurve(long int region, long int pnum)
{
    if (region == FLAGLONG)
    {
        const DoublePairVec & curve = m_allregioncurves[pnum].GetXYvec();
        return curve[0].first;
    }
    const DoublePairVec & curve = m_regioncurves[region][pnum].GetXYvec();
    return curve[0].first;
}

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

double BayesAnalyzer_1D::GetMaxParamValFromCurve(long int region, long int pnum)
{
    if (region == FLAGLONG)
    {
        const DoublePairVec & curve = m_allregioncurves[pnum].GetXYvec();
        return curve[curve.size()-1].first;
    }
    const DoublePairVec & curve = m_regioncurves[region][pnum].GetXYvec();
    return curve[curve.size()-1].first;
}

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

double BayesAnalyzer_1D::GetBinWidthFromCurve(long int region, long int pnum)
{
    if (region == FLAGLONG)
    {
        return m_allregioncurves[pnum].GetBinWidth();
    }
    return m_regioncurves[region][pnum].GetBinWidth();
}

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

bool BayesAnalyzer_1D::GetIsLog(long int pnum)
{
    bool isLog = m_allregioncurves[pnum].IsLog();
    long int numregions = m_regioncurves.size();
    for(long int regNo = 0; regNo < numregions; regNo++)
    {
        assert(isLog == m_regioncurves[regNo][pnum].IsLog());
    }
    return isLog;
}

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

long int BayesAnalyzer_1D::GetNumUniquePoints(long int pnum)
{
    return m_paramlikes[m_paramlikes.size()-1][pnum].GetNumUniquePoints();
}

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

double BayesAnalyzer_1D::GetKernelWidth(long int pnum)
{
    return m_paramlikes[m_paramlikes.size()-1][pnum].GetKernelWidth();
}

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

void BayesAnalyzer_1D::WriteCurvesForRegion(long int region)
{
    ofstream curvefile;
    curvefile.precision(10);
    string regname;
    if (region==FLAGLONG)
        regname = "overall_";
    else
        regname = "reg" + indexToKey(region) + "_";

    const ParamVector paramvec(true);
    unsigned long int chain = m_paramlikes.size()-1;
    for (long int pnum = 0; pnum<static_cast<long int>(paramvec.size()); pnum++)
    {
        if (!IsVariable(pnum)) continue;
        DoublePairVec curve;
        bool islog;
        if (region==FLAGLONG)
        {
            curve = m_allregioncurves[pnum].GetXYvec();
            islog = m_allregioncurves[pnum].IsLog();
        }
        else
        {
            curve = m_regioncurves[region][pnum].GetXYvec();
            islog = m_regioncurves[region][pnum].IsLog();
        }
        string pname = paramvec[pnum].GetShortName();
        string::size_type i = pname.find("/");
        while (i != string::npos)
        {
            pname.replace(i,1,"+");
            i = pname.find("/");
        }
        UserParameters& userparams = registry.GetUserParameters();
        string prefix = userparams.GetCurveFilePrefix();
        string fname = prefix + "_" + regname + pname + ".txt";
        curvefile.open(fname.c_str(), ios::out );
        userparams.AddCurveFileName(fname);
        //General info
        curvefile << "Bayesian likelihood curve for \""
                  << paramvec[pnum].GetName() << "\" ";
        if (region==FLAGLONG)
            curvefile << "as combined over all genomic regions.";
        else
            curvefile << "for genomic region " << (region + 1) << ".";
        curvefile << endl;

        if ((region != m_currentRegion-1) && (region != FLAGLONG))
        {
            registry.GetRunReport().ReportDebug("WriteCurvesForRegion can report more information when called on only"
                                                " the most recent region or for the overall region estimates.");
        }
        else if (region == FLAGLONG)
        {
            long int numregions = m_regioncurves.size();
            curvefile << "Created by averaging " << numregions
                      << " different regional curves." << endl;
        }
        else
        {
            long int numreplicates = m_replicatecurves[m_currentRegion-1].size();
            if (numreplicates > 1)
            {
                curvefile << "Created by averaging " << numreplicates
                          << " different replicate curves." << endl;
            }
            else
            {
                long int numpoints = m_paramlikes[chain][pnum].GetNumUniquePoints();
                double kernel = m_paramlikes[chain][pnum].GetKernelWidth();
                curvefile << "Created from " << numpoints
                          << " unique data points and a kernel width of "
                          << kernel << "." << endl;
            }
        }
        double lowerbound = paramvec[pnum].GetPrior().GetLowerBound();
        double upperbound = paramvec[pnum].GetPrior().GetUpperBound();
        curvefile << "The prior for this parameter was ";
        if (islog)
        {
            curvefile << "logarithmic";
        }
        else
        {
            curvefile << "flat";
        }
        curvefile << ", and ranged from "
                  << ToString(lowerbound) << " to "
                  << ToString(upperbound) << ".";
        if (islog)
        {
            curvefile << "  (Or, in log space, "
                      << ToString(SafeLog(lowerbound)) << " to "
                      << ToString(SafeLog(upperbound)) << ".)";
        }
        curvefile << endl;

        //Column headers
        if (islog)
            curvefile << "Ln(" << pname << ")";
        else
            curvefile << pname;
        curvefile << "\tLikelihood" << endl;
        //Raw data
        for (unsigned long int line = 0; line<curve.size(); line++)
            curvefile << curve[line].first << "\t" << curve[line].second << endl;
        curvefile << endl << endl;
        curvefile.close();
    }
} // WriteCurvesForRegion

//____________________________________________________________________________________
