// $Id: reportpage.cpp,v 1.130 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 */


// All versions of SetupColhdr() used for PrintTable() must
//   put "\n" at EOL for each line.  PrintTable() will not do it!
//
// UserOptions
//    infilenames, if > 1, print ugly

#include <cassert>
#include <iostream>

#include "analyzer.h"
#include "calculators.h"
#include "datatype.h"
#include "defaults.h"
#include "dlmodel.h"
#include "force.h"
#include "mathx.h"
#include "parameter.h"
#include "plotstat.h"
#include "region.h"
#include "registry.h"
#include "reportpage.h"
#include "runreport.h"
#include "stringx.h"
#include "timex.h"                      // for retrieving ending time stamp in MlePage::Show()
#include "types.h"
#include "vector_constants.h"
#include "vectorx.h"
#include "priorreport.h"                // for MlePage

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

using namespace std;

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

typedef vector < pair<unsigned long, double> > LDpairVec;
typedef vector < pair<unsigned long, double> >::iterator LDpairVecIt;

#define NOVAL  "*n/a*"                  //What to print if there's no value for a spot in the table.
#define ERRVAL "*err*"                  //What to print if there's no value for a spot in the table.

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

ReportPage::ReportPage(ofstream& pf, long pglngth, long pgwdth)
    : outf(pf)
{
    verbosity = registry.GetUserParameters().GetVerbosity();
    pagelength = pglngth;
    pagewidth = pgwdth;
    current_length = 0;
    m_bayesanalysis = registry.GetChainParameters().IsBayesian();
    if (m_bayesanalysis)
    {
        m_MLE = "MPE";
    }
    else
    {
        m_MLE = "MLE";
    }

} // ReportPage::ReportPage

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

string ReportPage::MakeLineOf(const char ch, long length, long indent)
{
    string line;
    long pos;

    for(pos = 0; pos < indent; ++pos)
        line += " ";

    for( ; pos < length; ++pos)
        line += ch;

    line += string("\n");

    return(line);

} // ReportPage::MakeLineOf

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

string ReportPage::MakePageBreak()
{
    string line = string("\n");

    return(line);

} // ReportPage::MakePageBreak

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

string ReportPage::MakeBlankLine()
{
    return(string("\n"));

} // ReportPage::MakeBlankLine

// GetOneRow takes 2d data and pulls out cross-slice of it.  Since the data
//  is normally stored in columns, this routine gives us a row.
StringVec1d ReportPage::GetOneRow(const StringVec2d table, const long rownum)
{
    StringVec1d row;
    for (unsigned long i=0; i<table.size(); ++i)
        row.push_back(table[i][rownum]);
    return row;
}

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

string ReportPage::MakeSimpleRow(vector<long>& colwdth,
                                 vector<string>& contents)
{
    string line;
    long col, ncols = colwdth.size();
    for(col = 0; col < ncols; ++col)
    {
        line += MakeJustified(contents[col],colwdth[col]);
    }
    line += "\n";

    return(line);

} // ReportPage::MakeSimpleRow

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

string ReportPage::MakeTwoCol(const vector<long>& colwdth,
                              const string& col1, const string& col2)
{
    // Left column; left justified
    string line = MakeJustified(col1,-1*colwdth[0]);
    // Right column; right justified
    line += " " + MakeJustified(col2,colwdth[1]);
    line += "\n";

    return(line);

} // ReportPage::MakeTwoCol

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

string ReportPage::MakeTwoCol(const vector<long>& colwdth,
                              const string& col1, const char* col2)
{
    return(MakeTwoCol(colwdth,string(col1),string(col2)));

} // ReportPage::MakeTwoCol

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

string ReportPage::MakeTwoCol(const vector<long>& colwdth,
                              const char* col1, const string& col2)
{

    return(MakeTwoCol(colwdth,string(col1),string(col2)));

} // ReportPage::MakeTwoCol

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

string ReportPage::MakeTwoCol(const vector<long>& colwdth,
                              const char* col1, const char* col2)
{
    return(MakeTwoCol(colwdth,string(col1),string(col2)));

} // ReportPage::MakeTwoCol

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

void ReportPage::PrintLineOf(const char ch, long length, long indent)
{
    outf << MakeLineOf(ch,length,indent);
    ++current_length;

} // ReportPage::PrintLineOf

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

void ReportPage::PrintBlankLine()
{
    //LS NOTE:  This is a very crude approximation of a decent page-break
    // algorithm, but that'd require a fair overhaul of the system as it is.
    if (current_length >= pagelength-3)
    {
        PrintPageBreak(); //resets current_length
        PrintTitle();
        PrintBlankLine();
    }
    else
    {
        outf << MakeBlankLine();
        ++current_length;
    }
} // ReportPage::PrintBlankLine

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

void ReportPage::PrintSimpleRow(vector<long> &colwdth,
                                vector<string> &contents)
{
    outf << MakeSimpleRow(colwdth,contents);
    ++current_length;

} // ReportPage::PrintSimpleRow

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

void ReportPage::PrintTwoCol(const vector<long> &colwdth, const string &col1,
                             const string &col2)
{
    outf << MakeTwoCol(colwdth, col1, col2);
    ++current_length;

} // ReportPage::PrintTwoCol

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

void ReportPage::PrintTwoCol(const vector<long> &colwdth, const char *col1,
                             const string &col2)
{
    outf << MakeTwoCol(colwdth, col1, col2);
    ++current_length;

} // ReportPage::PrintTwoCol

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

void ReportPage::PrintTwoCol(const vector<long> &colwdth, const string &col1,
                             const char *col2)
{
    outf << MakeTwoCol(colwdth, col1, col2);
    ++current_length;

} // ReportPage::PrintTwoCol

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

void ReportPage::PrintTwoCol(const vector<long> &colwdth, const char *col1,
                             const char *col2)
{
    outf << MakeTwoCol(colwdth, col1, col2);
    ++current_length;

} // ReportPage::PrintTwoCol

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

void ReportPage::PrintPageBreak()
{
    outf << "" << endl;
    current_length = 0;

} // ReportPage::PrintPageBreak

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

void ReportPage::Setup(vector<string> &title, long pglength,
                       long pgwidth, char tdlm)
{
    pagetitle = title;
    titledlm = tdlm;
    pagelength = pglength;
    pagewidth = pgwidth;

} // ReportPage::Setup

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

void ReportPage::Setup(string &title, long pglength,
                       long pgwidth, char tdlm)
{
    vector<string> title1;

    title1.push_back(title);
    pagetitle = title1;
    titledlm = tdlm;
    pagelength = pglength;
    pagewidth = pgwidth;

} // ReportPage::Setup

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

void ReportPage::Setup(const char *title, long pglength,
                       long pgwidth, char tdlm)
{
    vector<string> title1;
    string chartitle(title);

    title1.push_back(chartitle);
    pagetitle = title1;
    titledlm = tdlm;
    pagelength = pglength;
    pagewidth = pgwidth;

} // ReportPage::Setup

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

vector<string> ReportPage::MakeTitle()
{
    vector<string> title = pagetitle;
    vector<string>::iterator sit;

    // add linefeeds to the end of each element of title.
    for(sit = title.begin(); sit != title.end(); ++sit)
        *sit += "\n";

    // put title delimiter lines at begin and end of title
    title.insert(title.begin(),MakeLineOf(titledlm,pagewidth));
    title.push_back(MakeLineOf(titledlm,pagewidth));

    return(title);

} // ReportPage::MakeTitle

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

vector<string> ReportPage::MakeTableTitle(const string &title)
{
    vector<string> titlelines;
    titlelines.push_back(title+":");
    unsigned long linelength = title.size()+1;
    if (linelength > pagewidth)
        linelength = pagewidth-2;
    titlelines.push_back(MakeLineOf('-',linelength));

    return(titlelines);

} // ReportPage::MakeTableTitle

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

vector<string> ReportPage::MakeTableTitle(const char *title)
{
    return(MakeTableTitle(string(title)));

} // ReportPage::MakeTableTitle

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

void ReportPage::PrintTitle()
{
    PrintLineOf(titledlm,pagewidth);
    vector<string>::iterator sit;
    for(sit = pagetitle.begin(); sit != pagetitle.end(); ++sit)
    {
        outf << *sit << endl;
        ++current_length;
    }
    PrintLineOf(titledlm,pagewidth);

    sit = pagetitle.begin();
    if (sit->find("(cont.)") == string::npos)
        *sit += "\t(cont.)";
} // ReportPage::PrintTitle

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

void ReportPage::PrintCenteredString(const string &str, long width,
                                     long indent, bool trunc)
{
    string chunk = MakeCentered(str,width,indent,trunc);

    outf << chunk;
    ++current_length;

} // ReportPage::PrintCenteredString

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

void ReportPage::PrintCenteredString(const char *str, long width,
                                     long indent, bool trunc)
{
    const string pstr(str);

    PrintCenteredString(pstr,width,indent,trunc);

} // ReportPage::PrintCenteredString

void ReportPage::PrintWrapped(const string &line)
{
    StringVec1d wrappedline;
    wrappedline.push_back(line);
    wrappedline = Linewrap(wrappedline, pagewidth-2);
    outf << wrappedline[0] << endl;
    ++current_length;
    for (unsigned long i=1; i<wrappedline.size(); ++i)
    {
        outf << "  " << wrappedline[i] << endl;
        ++current_length;
        if (wrappedline[i].find("\n"))
            ++current_length;
    }
} // PrintWrapped

void ReportPage::PrintWrapped(const StringVec1d& lines)
{
    for (unsigned long nline=0; nline<lines.size(); nline++)
    {
        PrintWrapped(lines[nline]);
    }
} // PrintWrapped

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

void ReportPage::PrintTableTitle(const string &title)
{
    vector<string> titlelines = MakeTableTitle(title);
    vector<string>::iterator tit;

    for(tit = titlelines.begin(); tit != titlelines.end(); ++tit)
        PrintWrapped(*tit);

} // ReportPage::PrintTableTitle

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

void ReportPage::PrintTableTitle(const char *title)
{
    PrintTableTitle(string(title));

} // ReportPage::PrintTableTitle

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

string ReportPage::MakeSectionBreak(const char dlm, long width,
                                    long indent)
{
    return(MakeLineOf(dlm,width,indent));

} // ReportPage::MakeSectionBreak

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

void ReportPage::PrintSectionBreak(const char dlm, long width,
                                   long indent)
{
    outf << MakeSectionBreak(dlm,width,indent);
    ++current_length;

} // ReportPage::PrintSectionBreak

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

// colhdr is dimensioned: line
// rowhdr is dimensioned: section X row
// colwdth is dimensioned: col
// innards is dimensioned: section X row X col

vector<string> ReportPage::MakeTable(StringVec1d& colhdr, StringVec2d& rowhdr,
                                     LongVec1d& colwdth, StringVec3d& innards)
{
    StringVec1d table;
    long hdrwdth = colwdth[0];

    long ncols = colwdth.size();
    long totlength = 0;
    for(long col = 0; col < ncols; ++col)
        totlength += colwdth[col];

    vector<string>::iterator lit;
    for(lit = colhdr.begin(); lit != colhdr.end(); ++lit)
        table.push_back(*lit);
    //table.push_back(MakeLineOf('-',totlength));

    long sect, nsect = innards.size(), sectindent=3;
    for(sect = 0; sect < nsect; ++sect)
    {
        if (sect != 0)
            table.push_back(MakeSectionBreak('-',totlength,sectindent));
        table.push_back(rowhdr[sect][0]+"\n");
        long line, nlines = rowhdr[sect].size();
        for(line = 1; line < nlines; ++line)
        {
            string tline;
            tline = MakeCentered(rowhdr[sect][line],hdrwdth);
            long col, ncols = innards[sect][line].size();
            for(col = 0; col < ncols; ++col)
            {
                tline += MakeCentered(innards[sect][line][col],colwdth[col+1]);
            }
            table.push_back(tline + "\n");
        }
    }

    table.push_back(MakeLineOf('-',totlength));

    return(table);

} // ReportPage::MakeTable

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

// colhdr is dimensioned: line
// rowhdr is dimensioned: section X row
// colwdth is dimensioned: col
// innards is dimensioned: section X row X col

void ReportPage::PrintTable(vector<string> &colhdr, StringVec2d &rowhdr,
                            vector<long> &colwdth,  StringVec3d &innards)
{
    vector<string> table = MakeTable(colhdr,rowhdr,colwdth,innards);
    vector<string>::iterator line;

    for(line = table.begin(); line != table.end(); ++line)
    {
        outf << *line;
        ++current_length;
    }
} // ReportPage::PrintTable

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

double ReportPage::GetCentile(const vector<centilepair>& centiles,
                              double pcent)
{
    vector<centilepair>::const_iterator cent;
    for(cent = centiles.begin(); cent != centiles.end(); ++cent)
    {
        if (!CloseEnough(cent->first, pcent)) continue;
        return cent->second;
    }

    assert(false);  // never found a needed centile!
    return FLAGDOUBLE;

} // ReportPage::GetCentile

double ReportPage::GetReverseCentile(const vector<centilepair>& centiles,
                                     double pcent)
{
    vector<centilepair>::const_iterator cent;
    //For a bayesian fixed analysis, we need to search the other way 'round.
    for(cent = centiles.begin(); cent != centiles.end(); ++cent)
    {
        if (!CloseEnough(cent->second, pcent)) continue;
        return cent->first;
    }

    assert(false);  // never found a needed centile!
    return FLAGDOUBLE;

} // ReportPage::GetCentile

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

StringVec2d ReportPage::SortTable(StringVec2d intable, unsigned long sortcol,
                                  unsigned long headerrows)
{
    if(intable.size() < sortcol)
    {
        registry.GetRunReport().ReportDebug("What the hey?  Tried to sort on a nonexistant column.  Last time this"
                                            " happened was because a force was on that had no valid parameters.");
        return intable;
    }
    sortcol--; //to change to an index instead of a column number.
    if (intable[sortcol].size() <= headerrows)
        return intable;

    StringVec2d outtable;

    string sortme = "";
    for (unsigned long row=headerrows; row<intable[sortcol].size(); ++row)
    {
        sortme += intable[sortcol][row] + " ";
    }
    DoubleVec1d tosort;
    try
    {
        tosort = StringToDoubleVecOrBarf(sortme);
    }
    catch(const exception& ex)
    {
        string msg = ex.what();
        msg += ":  Unable to sort this table.  We probably did this on purpose.";
        RunReport& runreport = registry.GetRunReport();
        runreport.ReportDebug(msg);
        return intable;
    }

    LDpairVec unsorted;
    LongVec1d sorted;
    for (unsigned long i=0; i<tosort.size(); ++i)
        unsorted.push_back(make_pair(i, tosort[i]));

    while (unsorted.size() > 0)
    {
        LDpairVecIt smallest_it = unsorted.begin();
        for (LDpairVecIt i=unsorted.begin()++; i!=unsorted.end(); i++)
            if (i->second < smallest_it->second)
                smallest_it = i;
        sorted.push_back(smallest_it->first);
        unsorted.erase(smallest_it);
    }

    //LS TEST:  the sorted lines
#if 0
    for (unsigned long row=headerrows; row<intable[sortcol].size(); ++row)
        cout << row << ":  " << sorted[row-headerrows] << ", "
             << intable[sortcol][sorted[row-headerrows]+headerrows] << endl;
#endif

    for (unsigned long col=0; col<intable.size(); ++col)
    {
        StringVec1d newcolumn;
        for (unsigned long row=0; row<headerrows; ++row)
            newcolumn.push_back(intable[col][row]);
        for (unsigned long row=headerrows; row<intable[sortcol].size(); ++row)
            newcolumn.push_back(intable[col][sorted[row-headerrows]+headerrows]);
        outtable.push_back(newcolumn);
    }
    return outtable;
}

// TrimString makes a column label a bit shorter, but in a way that doesn't
//  reduce the amount of information in the title.  In other words, TrimString
//  is used to make a column header that *doesn't* need a legend describing
//  what was trimmed.  If you do need a legend, use MakeItShorter.
void ReportPage::TrimString(string& title)
{
    string::size_type i;

    //First try taking out double spaces.
    i = title.find("  ");
    while (i != string::npos)
    {
        title.erase(i, 1);
        i = title.find("  ");
    }

    //Now take out leading or trailing spaces.
    i = title.find(" ");
    while (i == 0)
    {
        title.erase(0,1);
        i = title.find(" ");
    }

    if (title.size()>0)
        while ((title[title.size()-1]==' ') && title.size()>1)
            title.erase(title.size()-1,1);

    //Now try taking out the string "Population " from the title.
    i = title.find("Population ");
    while (i != string::npos)
    {
        title.erase(i, 11);
        i = title.find("Population ");
    }

    return;
}

// MakeItShorter takes a section header and tries to make it fit into the
//  given width.  If that doesn't work, it just truncates and returns false,
//  telling the calling routine that it was not able to reduce the length
//  of the string without the possibility of losing important information
//  (if, say, the given title matches another title for the first 'width'
//  characters, and only differs in the now-truncated bit.)  If you can think
//  of other ways to truncate titles, be my guest and add 'em here.
bool ReportPage::MakeItShorter(string& title, const unsigned long width)
{
    string::size_type i, j;
    if (title.size() == 0)
        return false;

    //Try removing the "From " / " to " found in migration titles.
    i = title.find("From ");
    j = title.find(" to ");
    while ((i != string::npos) && (j != string::npos))
    {
        title.replace(j, 4, "-");
        title.erase(i, 5);
        i = title.find("From ");
        j = title.find(" to ");
    }
    if (title.size() <= width)
        return true;

    if (title.find("/") != string::npos)
    {
        //This is probably the shortname of a combined parameter.  We check
        // this again to make sure there are actually multiples.
        //Change 'Theta' to 'T'
        i = title.find("Theta");
        if (i != title.rfind("Theta"))
        {
            while (i != string::npos)
            {
                title.erase(i+1,4);
                i = title.find("Theta");
            }
            if (title.size() <= width) return true;
        }
        //Change 'Growth' to 'G'
        i = title.find("Growth");
        if (i != title.rfind("Growth"))
        {
            while (i != string::npos)
            {
                title.erase(i+1,5);
                i = title.find("Growth");
            }
            if (title.size() <= width) return true;
        }
    }
    //If there's a '/N#' (where N is a capital letter and # is a number),
    // reduce it to just '/#'.
    i = title.find("/");
    if (i != string::npos) i++;
    while (i != string::npos)
    {
        if ((title[i] >= 'A') && (title[i] <= 'Z'))
        {
            i++;
            if (i != string::npos)
            {
                if ((title[i] >= '0') && (title[i] <= '9'))
                {
                    title.erase(i-1,1);
                }
            }
        }
        i = title.find("/",i);
        if (i != string::npos) i++;
    }
    if (title.size() <= width)
        return true;

    //Give up.
    title.assign(title, 0, width);
    return false;
}

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

MlePage::MlePage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth), hdrindent(3)
{
    label_colwidth = 14;
    colwidth = 10; //Columns will be printed with spaces between 'em.

} // MlePage::MlePage

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

void MlePage::Show()
{
    // General Progam header goes here for now
    {
        long shortlinelength = 60;
        string line;

        PrintLineOf('*',shortlinelength);
        line.assign("LAMARC:  Maximum Likelihood Parameter Estimation\n");
        PrintCenteredString(line,shortlinelength);
        line.assign("using Hastings-Metropolis Markov Chain Monte Carlo\n");
        PrintCenteredString(line,shortlinelength);
        PrintLineOf('*',shortlinelength);
        outf << "version " << VERSION << endl;
        ++current_length;

        // Appends indication of any debugging options which may different from the "usual" configuration.
        if (DebuggingOptionsRunning())
        {
            outf << DebuggingOptionsString(current_length);
        }

        PrintBlankLine();
        PrintBlankLine();
        line = "Program started on " +
            PrintTime(registry.GetUserParameters().GetProgramStartTime(),"%c")
            + "\n";
        PrintCenteredString(line,shortlinelength);
        line = "finished on " + PrintTime(GetTime(),"%c") + "\n";
        PrintCenteredString(line,shortlinelength,3L);
        PrintBlankLine();
    }

    // Begin actual MlePage output
    PrintTitle();
    PrintBlankLine();
    PrintBlankLine();

    // print a MLE table for each force
#ifndef JSIM
    WriteBody();
#else
    WriteSimMles();
#endif
    PrintPageBreak();

} // MlePage::Show

// WriteBody is the main engine for creating the MLE output page.  It loops
//  over all the forces, sticking them in the 3d string vector 'allforcetable'
//  and when it's done, calls the routines that format and print it.  Both
//  the data and the labels are stored in 'allforcetable'.
void MlePage::WriteBody()
{
    const ForceVec forces = registry.GetForceSummary().GetAllForces();
    StringVec3d allforcetable;
    //All the data gets put in here.  The dimensions are [table][column][row].

    Strmap namemap;
    // The name map has a long name first, and a short name second.  If the
    // short name is longer than colwidth, another entry with that name first
    // and an even shorter name second should be provided.  The shorter names
    // are used when the amount of space of a column or columns is too small.
    // The namemap is used to create legends for the tables.

    ForceVec::const_iterator fit;
    for(fit = forces.begin(); fit != forces.end(); ++fit)
    {
        if ((*fit)->HasNoVaryingParameters()) continue;

        string forcename = (*fit)->GetFullparamname();
        string shortforcename = (*fit)->GetShortparamname();
        force_type tag = (*fit)->GetTag();
        namemap.insert(make_pair(forcename, shortforcename));

        bool usepercentiles = true;
        if ((*fit)->SummarizeProfTypes() != profile_PERCENTILE) usepercentiles = false;

        DoubleVec1d modifiers = registry.GetForceSummary().GetModifiersByForce(tag);

        StringVec1d subtypes = (*fit)->GetAxisname();
        //Probably ("Population", "Region")
        long titleindex = 0;
        StringVec1d firstcolumn = MakeLabels(subtypes, modifiers, usepercentiles);
        StringVec2d forcetable;
        forcetable.push_back(firstcolumn);

        const vector<Parameter>& parameters = (*fit)->GetParameters();
        vector<Parameter>::const_iterator param;
        for (param = parameters.begin(); param != parameters.end(); ++param)
        {
            ParamStatus mystatus = param->GetStatus();
            if (!mystatus.Varies()) continue;

            string paramname = param->GetName();
            titleindex++;
            //It would be nice if the 'section title' was stored in the parameter
            // itself instead of in the force, but we do what we can do.

            // Now store a 'chain' of progressively shorter names in the namemap.
            // When printing it out, we have an as-of-now-unknown amount of space
            // in which to print it.  See MakeItShorter for details.

            TrimString(paramname);
            string shortname = paramname;
            string lastname = paramname;
            while (MakeItShorter(shortname, shortname.size()-1))
            {
                namemap.insert(make_pair(lastname, shortname));
                lastname = shortname;
            }
            shortname = param->GetShortName();
            namemap.insert(make_pair(lastname, shortname));
            lastname = shortname;
            //We don't go to GetShortName immediately because often times the
            // parameter short name is somewhat cryptic; i.e. 'Theta1' when we're
            // actually looking for a short version of the *population* name.
            if (shortname.size() > colwidth-1)
            {
                lastname = shortname;
                MakeItShorter(shortname, colwidth-1);
                namemap.insert(make_pair(lastname, shortname));
            }
            //That's as short as we'd ever want, though it's a straight truncation.

            long nregs = registry.GetDataPack().GetNRegions();
            for (long reg = 0; reg <= nregs; ++reg)
            {
                bool do_overall(false);
                if (reg==nregs)
                {
                    if (nregs <= 1)
                    {
                        break;
                    }
                    else
                    {
                        do_overall = true;
                    }
                }
                string regionname = "Overall";
                double MLE = param->GetOverallMLE();
                if (!do_overall)
                {
                    regionname = registry.GetDataPack().GetRegion(reg).GetRegionName();
                    if (namemap.find(regionname) == namemap.end())
                    {
                        string shortregionname = "reg";
                        shortregionname += indexToKey(reg);
                        namemap.insert(make_pair(regionname, shortregionname));
                    }
                    MLE = param->GetMLE(reg);
                }

                StringVec1d percentiles;
                if (usepercentiles)
                    //if (usepercentiles && (param->GetProfileType() != profile_NONE) )
                {
                    percentiles = DoPercentiles(reg, param, modifiers, namemap);
                }
                StringVec1d newcolumn = MakeColumn(forcename, paramname, regionname,
                                                   MLE, percentiles, usepercentiles);
                forcetable.push_back(newcolumn);
            }
        }
        AddForceToOutput(allforcetable, forcetable);
    }

    const RegionGammaInfo *pRegionGammaInfo = registry.GetRegionGammaInfo();
    if (pRegionGammaInfo)
    {
        DoRegionGammaInfo(allforcetable, namemap, pRegionGammaInfo);
    }

    WrapOutput(allforcetable);
    WriteOutput(allforcetable, namemap);
}

void MlePage::DoRegionGammaInfo(StringVec3d& allforcetable, Strmap& namemap,
                                const RegionGammaInfo *pRegionGammaInfo)
{
    if (!pRegionGammaInfo)
        throw implementation_error("MlePage::DoRegionGammaInfo() received a NULL RegionGammaInfo pointer.");
    const ParamVector pvec(true);
    vector<Parameter> dummy;
    long indexOfAlpha = pvec.size() - 1;
    dummy.push_back(pvec[indexOfAlpha]);
    vector<Parameter>::const_iterator alpha_it = dummy.begin();
    DoubleVec1d modifiers = registry.GetForceSummary().GetModifiers(indexOfAlpha);
    StringVec1d percentiles;
    bool usePercentiles = profile_PERCENTILE == pRegionGammaInfo->GetProfType();
    StringVec1d subtypes;
    subtypes.push_back("");
    subtypes.push_back(""); // deliberately empty for gamma
    StringVec1d labelColumn = MakeLabels(subtypes, modifiers, usePercentiles);
    StringVec2d forcetable;
    forcetable.push_back(labelColumn);
    if (usePercentiles)
        percentiles = DoPercentiles(registry.GetDataPack().GetNRegions(),
                                    alpha_it, modifiers, namemap);
    StringVec1d dataColumn = MakeColumn("RegGamma", "alpha", "Overall",
                                        pRegionGammaInfo->GetMLE(),
                                        percentiles, usePercentiles);
    forcetable.push_back(dataColumn);
    AddForceToOutput(allforcetable, forcetable);
}

// erynes extracted this method from MleBody::WriteBody(),
// so that it could be called a second time to report the
// gamma results, when gamma is present.
StringVec1d MlePage::DoPercentiles(long region,
                                   vector<Parameter>::const_iterator param,
                                   const DoubleVec1d& modifiers,
                                   Strmap& namemap)
{
    StringVec1d percentiles;
    vector<centilepair> CIvec;
    if (region == registry.GetDataPack().GetNRegions() &&
        region > 1)
        CIvec = param->GetOverallCIs();
    else
        CIvec = param->GetCIs(region);

    for (unsigned long ind = 0; ind < modifiers.size(); ++ind)
    {
        if (profile_NONE == param->GetProfileType())
        {
            percentiles.push_back(MakeCentered(NOVAL,colwidth-2));
            continue;
        }
        double centile = GetCentile(CIvec, modifiers[ind]);
        string s_centile = Pretty(centile, colwidth-2);

        //If the profiler gave up before finding values with as low a
        // log likelihood as it would have liked, that information was
        // saved in the analyzer, and we can get it out here to tell
        // users that the value is "<1.53" instead of "1.53".
        if (param->CentileIsExtremeLow(modifiers[ind], region))
        {
            s_centile = "<" + Pretty(centile, colwidth-3);
            if (param->CentileIsExtremeHigh(modifiers[ind], region))
            {
                //Special flag condition when maximization failed.
                s_centile = ERRVAL;
            }
        }
        else if (param->CentileIsExtremeHigh(modifiers[ind], region))
        {
            s_centile = ">" + Pretty(centile, colwidth-3);
        }
        else if (param->CentileHadWarning(modifiers[ind], region))
        {
            s_centile = "*" + Pretty(centile, colwidth-3);
            namemap.insert(make_pair("*", "This profile value had a warning from the maximizer, "
                                     "probably a failure to converge after a large number of iterations."));
        }
        percentiles.push_back(s_centile);
    }
    return percentiles;
}

// MakeLabels makes a 1D vector that will act as the first column for a table
//  in WriteBody out of 'subtypes' (from force.GetAxisName()), and a vector
//  of modifiers, if indeed percentiles are being used.
StringVec1d MlePage::MakeLabels(const StringVec1d subtypes, const DoubleVec1d modifiers, const bool usepercentiles)
{
    assert (subtypes.size() == 2);

    StringVec1d column;
    column.push_back(""); // Or could be "Parameter"; it's the force title line.
    column.push_back(subtypes[0]);
    column.push_back(subtypes[1]);
    string MLEtitle = "Best Val (" + m_MLE + ")";
    column.push_back(MLEtitle);
    if (usepercentiles)
    {
        column.push_back("Percentile");
        StringVec1d percentiles(VecElemToString(modifiers));
        unsigned long length = percentiles.size();
        for (unsigned long i=0; i<length; ++i)
        {
            string fullnum = "0.000";
            fullnum.replace(0, percentiles[i].size(),percentiles[i]);
            double percent = 100 * fabs(.5 - modifiers[i])*2;
            string final = ToString(percent);
            if (percent > 0)
                final += "%   " + fullnum;
            else
                final = fullnum;
            column.push_back(final);
            if ((length > 5) && (i==length/2-1))
                column.push_back(m_MLE);
        }
    }
    return column;
}

// MakeColumn makes a 1D vector that acts as the body column for a table in
//  in WriteBody.  All columns have all their labels; they aren't concatenated
//  until later, so we can accurately wrap the table first.

StringVec1d MlePage::MakeColumn(const string& forcename, const string& paramname,
                                const string& regionname, const double MLE,
                                const StringVec1d& percentiles,
                                const bool usepercentiles)
{
    StringVec1d column;
    column.push_back(forcename);
    column.push_back(paramname);
    column.push_back(regionname);
    column.push_back(Pretty(MLE, colwidth-2));
    if (usepercentiles)
    {
        column.push_back("");
        unsigned long length = percentiles.size();
        for (unsigned long i=0; i<length; ++i)
        {
            column.push_back(percentiles[i]);
            if ((length > 5) && (i==length/2-1))
                column.push_back(Pretty(MLE, colwidth-2));
        }
    }
    return column;
}

// AddForceToOutput takes a 2D table containing all the information for a
//  particular force, and adds it to 'allforcetable'.  The trick is that if
//  the first colum of the new table matches the first column in one of the
//  existing tables, it's appended to the end of the old table, to be wrapped
//  later.  If nothing matches, it's added as a new table in the third
//  dimension.

void MlePage::AddForceToOutput(StringVec3d& allforcetable, StringVec2d forcetable)
{
    for (unsigned long table=0; table<allforcetable.size(); ++table)
    {
        if (DoColumnsMatch(allforcetable[table][0], forcetable[0]))
        {
            //The new force has the same labels as a previous force, so can
            //be added as a new column.
            for (unsigned long col=1; col<forcetable.size(); ++col)
                allforcetable[table].push_back(forcetable[col]);
            return;
        }
    }
    //The new force has new labels, so cannot go in the same table.
    allforcetable.push_back(forcetable);
    return;
}

// DoColumnsMatch checks if, well, the columns match.  Exactly.
bool MlePage::DoColumnsMatch(const StringVec1d col1, const StringVec1d col2)
{
    if (col1.size() != col2.size())
        return false;
    for (unsigned long row=0; row<col1.size(); ++row)
        if (col1[row] != col2[row])
            return false;

    return true;
}

// WrapOutput is a fairly complex routine that takes a bunch of 2d tables and
//  makes sure each table can fit widthwise on a page.  If it doesn't, it
//  examines the content of the first three rows, which are all labels (the
//  first saying which force it is, the second which partition or cross-
//  partition (aka 'Population'), and the third which region.  If a row is
//  found with identical labels across it, the wrapping routine is constrained
//  to not break up those columns as to create a 'widow' or 'orphan'--a column
//  broken off from its logical neighbors.  This means that 2-column sections
//  may not be broken up, nor may 3-column sections, that 4-column sections may
//  be broken exactly in their center, and so on.  Given the nature of the
//  data, there should be no possible input here that allows no wrapping at
//  all, but should such data be encountered, it will print a warning to the
//  user, and simply wrap the data at the max number of columns.
void MlePage::WrapOutput(StringVec3d& allforcetable)
{
    StringVec3d newforcetable;

    unsigned long maxcols = (pagewidth - label_colwidth + 1)/(colwidth+1);
    //maxcols should be 6 for pagewidth 75, label_colwidth=14, colwidth=10
    //
    //Note that the +1 is to compensate for the (unprinted) last space after
    // the last column (normally accounted for with colwidth+1)
    //
    // Use +5 instead of +1 to get a full-width column

    for (unsigned long table = 0; table < allforcetable.size(); ++table)
    {
        if (allforcetable[table][1].size() > 6)
            allforcetable[table] = SortTable(allforcetable[table], 2, 5);
        if (allforcetable[table].size()-1 < maxcols)
        {
            newforcetable.push_back(allforcetable[table]);
        }
        else
        {
            LongVec2d allbreaks;
            for (unsigned long row=0; row < 3; ++row)
            {
                LongVec1d breaks;
                string prev = allforcetable[table][1][row];
                long samelen = 1;
                for (unsigned long column=2; column < allforcetable[table].size(); ++column)
                {
                    if (allforcetable[table][column][row] == prev)
                    {
                        samelen++;
                    }
                    else
                    {
                        breaks.push_back(samelen);
                        samelen = 1;
                    }
                    prev = allforcetable[table][column][row];
                }
                breaks.push_back(samelen);
                allbreaks.push_back(breaks);
            }
            //'allbreaks' now contains three vectors of where the 'natural' break
            // points are.  Given the widow/orphan rules:
            //   size-1 groups can be broken up wherever.
            //   size-2 and -3 groups must be contiguous.
            //   size-4 and greater groups may be broken in such a way as to
            //     always keep at least 2 columns together.
            //
            // We now make a boolean vector that tells us where we may break up our
            // columns.

            vector<bool> allowedbreaks(allforcetable[table].size()-2);
            //'allowedbreaks' indicate if the column after the ab index (in the
            // body of the data; not the label column) may be wrapped.
            for (unsigned long col=0; col<allowedbreaks.size(); ++col)
                allowedbreaks[col]=true;
            for (long row=0; row<3; row++)
            {
                long actualcol=0;
                for (unsigned long breakcol=0; breakcol < allbreaks[row].size(); ++breakcol)
                {
                    long grouplen = allbreaks[row][breakcol];
                    if (grouplen > 1)
                    {
                        allowedbreaks[actualcol] = false;
                        allowedbreaks[actualcol + grouplen - 2] = false;
                        //grouplen is a size, not an index; hence the -2.
                    }
                    actualcol += grouplen;
                }
            }

            //LS TEST -- printing routine
#if 0
            cout << "Allowed breaks vector:" << endl;
            for (unsigned long i=0; i<allowedbreaks.size(); ++i)
                cout << allowedbreaks[i];
            cout << endl;
#endif

            //Now to actually wrap the darn table.  Start by checking at maxcols
            // and go down from there.

            unsigned long lastwrap = 0;

            for (unsigned long column = maxcols; column < allforcetable[table].size()+maxcols-1; column += maxcols)
            {
                StringVec2d newtable;
                if (column < allforcetable[table].size()-1)
                {
                    while ((column > lastwrap) && (allowedbreaks[column-1]==false))
                        --column;
                    if (column==lastwrap)
                    {
                        RunReport& runreport = registry.GetRunReport();
                        runreport.ReportChat("Couldn't find a place to wrap columns for output."
                                             "  Just using the max instead.");
                        column += maxcols;
                    }
                }
                else
                    column = allforcetable[table].size()-1;
                newtable.push_back(allforcetable[table][0]);
                for (unsigned long newcol = lastwrap+1; newcol<=column; ++newcol)
                    newtable.push_back(allforcetable[table][newcol]);
                lastwrap = column;
                newforcetable.push_back(newtable);
            }
        }
    }
    allforcetable = newforcetable;
}

// WriteOutput takes correctly-wrapped input data, formats the labels,
//  creates a legend if needed, and then prints the data to the output file.
//  It attempts a very crude page-wrapping mechanism wherein if the number
//  of columns would put us over the page limit, it prints a page break
//  and the title again.  This needs work if we want appropriate line
//  breaks in the output, but probably not here.
void MlePage::WriteOutput(StringVec3d& allforcetable, Strmap namemap)
{
    for (unsigned long table=0; table<allforcetable.size(); ++table)
    {
        if (pagelength < current_length)
        {
            registry.GetRunReport().ReportDebug
                ("Printed too many lines per page in the output file.");
        }
        if (pagelength < current_length + allforcetable[table][0].size())
        {
            PrintPageBreak();
            PrintTitle();
        }
        StrPairVec legend;
        StrPairVecIter legendline;

        // pull warning message out of namemap and munge onto legend
        // note: putting the warning into namemap in the first place
        // is a sad, sad hack which results in the error being repeated
        // on every table
        Strmapiter asterisk = namemap.find("*");
        if (asterisk != namemap.end())
        {
            legend.push_back(make_pair(asterisk->second, asterisk->first));
        }

        //We go through the first three rows individually, making sure not to
        // 'double-print' any labels.  This gives us enough space to print
        // the long name instead of resorting to the short name all the time.
        vector<bool> breaks;
        for (unsigned long i=0; i<allforcetable[table].size(); ++i)
        {
            breaks.push_back(false);
        }
        for (long titlerow=0; titlerow<3; ++titlerow)
        {
            if ((titlerow !=2) || (registry.GetDataPack().GetNRegions() > 1))
            {
                StringVec1d newrow = GetOneRow(allforcetable[table], titlerow);
                NixRedundancy(newrow, legend, namemap, breaks);
                WriteLine(newrow);
            }
        }

        for (unsigned long row=3; row<allforcetable[table][0].size(); ++row)
        {
            outf << MakeJustified(allforcetable[table][0][row], label_colwidth);
            for (unsigned long column=1; column<allforcetable[table].size(); ++column)
            {
                if (breaks[column])
                    outf << "|";
                else
                    outf << " ";
                outf << MakeCentered(allforcetable[table][column][row], colwidth);
            }
            outf << endl;
            ++current_length;
        }
        for (legendline=legend.begin(); legendline != legend.end(); legendline++)
        {
            string line = legendline->second;
            line += ":  " + legendline->first;
            PrintWrapped(line);
        }
        PrintBlankLine();
        PrintBlankLine();
    }
}

// NixRedundancy takes a row of strings, many of which will be the same, and
//  eliminates and centers all the redundant titles into just one title.
//
//  If all that's left is one title, the first string in the vector is also
//  replaced by that redundant title.

void MlePage::NixRedundancy(StringVec1d& row, StrPairVec& legend,
                            Strmap namemap, vector<bool>& breaks)
{
    string test = row[1];
    unsigned long width = colwidth;

    StringVec1d newrow;
    string title = MakeJustified(row[0], -label_colwidth);
    title += " ";
    newrow.push_back(title);
    vector<unsigned long> widths;
    StringVec1d pipesorspaces;
    long legendlevel = 0;
    widths.push_back(label_colwidth);
    pipesorspaces.push_back(" ");
    for (unsigned long column=2; column<row.size(); ++column)
    {
        if (row[column] == test)
            width += colwidth+1;
        else
        {
            //We have a row with more than one title
            title = test;
            if (title.size() > width)
            {
                long thislevel = 1;
                string old = title;
                Strmapiter mapline=namemap.find(title);
                while (mapline != namemap.end() && title.size() > width)
                {
                    title = mapline->second;
                    mapline = namemap.find(title);
                    thislevel++;
                }
                if (thislevel > legendlevel)
                    legendlevel = thislevel;
            }
            //title = MakeCentered(title, width);
            if (width > colwidth)
            {
                breaks[column] = true;
                pipesorspaces.push_back("|");
            }
            else if (breaks[column])
                pipesorspaces.push_back("|");
            else
                pipesorspaces.push_back(" ");
            newrow.push_back(test);
            widths.push_back(width);
            test = row[column];
            width = colwidth;
        }
    }
    //The last row's title still needs to be added to newrow
    title = test;
    if (title.size() > width)
    {
        long thislevel = 1;
        string old = title;
        Strmapiter mapline=namemap.find(title);
        while (mapline != namemap.end() && title.size() > width)
        {
            title = mapline->second;
            mapline = namemap.find(title);
            thislevel++;
        }
        if (thislevel > legendlevel)
            legendlevel = thislevel;
    }
    newrow.push_back(test);
    widths.push_back(width);
    pipesorspaces.push_back("");
    for (unsigned long i=1; i<newrow.size(); i++)
    {
        string old = newrow[i];
        for (long ll=1; ll < legendlevel; ll++)
        {
            Strmapiter mapline = namemap.find(newrow[i]);
            if (mapline != namemap.end())
                newrow[i] = mapline->second;
        }
        Strmapiter mapline = namemap.find(newrow[i]);
        while ((newrow[i].size() > widths[i]) && (mapline != namemap.end()))
        {
            //This should never be reached, but just in case.
            newrow[i] = mapline->second;
            mapline = namemap.find(newrow[i]);
        }
        if (old != newrow[i])
            legend.push_back(make_pair(old, newrow[i]));
        newrow[i] = MakeCentered(newrow[i],widths[i]) + pipesorspaces[i];
    }

    row = newrow;
    //Warning:  This routine requires that the 'depth' of the chained legend
    // associations in 'namemap' be equivalent for each title on the row.  If
    // not, it'll work, but might look funny and/or have mismatched titles
    // across the row.  For reliable but potentially mismatched code, try
    // the version just before this, before 5/28/04

} // MlePage::NixRedundancy()

// WriteLine takes a string vector and prints it, not adding spaces between the
//  columns and a line return at the end.
void MlePage::WriteLine(const StringVec1d line)
{
    if (line.size())
        outf << line[0];
    for (unsigned long column=1; column<line.size(); ++column)
        outf << line[column];
    outf << endl;
    ++current_length;
}

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

void MlePage::WriteSimMles()
{
    // loop over all forces, printing a set of line:
    //    forcename, parameter name, region/overall, mle value, 95% CI's
    //    forcename, parameter name, 90% CI's, 95% exclude, 90% exclude
    //
    //  finally print the number of variable sites (it may not be present
    //  if "verbose" output was not chosen).

    const ForceVec forces(registry.GetForceSummary().GetAllForces());
    const StringVec1d regnames(registry.GetDataPack().GetAllRegionNames());

    ForceVec::const_iterator fit;
    for(fit = forces.begin(); fit != forces.end(); ++fit)
    {
        DoubleVec2d mles((*fit)->GetMles());
        DoubleVec1d overallmles((*fit)->GetPopmles());
        string fname((*fit)->GetShortparamname());
        StringVec1d pnames((*fit)->GetAllParamNames());

        assert(mles.size() == overallmles.size() &&
               mles.size() == static_cast<unsigned long>((*fit)->GetNParams()));

        const vector<Parameter>& parameters = (*fit)->GetParameters();
        bool usepercentiles = true;
        if ((*fit)->SummarizeProfTypes() != profile_PERCENTILE) usepercentiles = false;

        unsigned long param, nparam((*fit)->GetNParams());
        for(param = 0; param < nparam; ++param)
        {
            if (pnames[param].empty())  // flag for invalid parameter
                continue;                // supported by Force::GetAllParamNames()
            string baseline(fname + " | " + pnames[param]);
            string baseline2(fname + " | " + pnames[param]);
            assert(mles[param].size() == regnames.size());
            unsigned long region;
            vector<centilepair> CIvec;
            for(region = 0; region < mles[param].size(); ++region)
            {
                string line(baseline);
                line +=  " | " + regnames[region];
                line += ": " + ToString(mles[param][region]);

                string line2(baseline2);
                if (usepercentiles)
                {
                    CIvec = parameters[param].GetCIs(region);
                    double lower(GetCentile(CIvec,0.025)), upper(GetCentile(CIvec,0.975));
                    double truevalue(parameters[param].GetTruth());
                    bool exclude95(lower < truevalue && truevalue < upper);
                    line += "; " + ToString(lower) + "->" + ToString(upper);
                    double lower2(GetCentile(CIvec,0.05)), upper2(GetCentile(CIvec,0.95));
                    bool exclude90(lower2 < truevalue && truevalue < upper2);
                    line2 += " 90% " + ToString(lower2) + "=>" + ToString(upper2);
                    line2 += "$ x95=" + ToString(exclude95);
                    if (!exclude95)
                    {
                        if (truevalue < lower) line2 += "Below";
                        else line2 += "Above";
                    }
                    line2 += "# x90=" + ToString(exclude90);
                }

                outf << line << endl;
                if (usepercentiles) outf << line2 << endl;
            }
            string line(baseline);
            line += " | Overall: " + ToString(overallmles[param]);

            string line2(baseline2);
            if (usepercentiles)
            {
                CIvec = parameters[param].GetOverallCIs();
                double lower(GetCentile(CIvec,0.025)), upper(GetCentile(CIvec,0.975));
                line += "; " + ToString(lower) + "->" + ToString(upper);
                double truevalue(parameters[param].GetTruth());
                bool exclude95(lower < truevalue && truevalue < upper);
                double lower2(GetCentile(CIvec,0.05)), upper2(GetCentile(CIvec,0.95));
                bool exclude90(lower2 < truevalue && truevalue < upper2);
                line2 += " 90% " + ToString(lower2) + "=>" + ToString(upper2);
                line2 += "$ x95=" + ToString(exclude95);
                if (!exclude95)
                {
                    if (truevalue < lower) line2 += "Below";
                    else line2 += "Above";
                }
                line2 += "# x90=" + ToString(exclude90);
            }

            outf << line << endl;
            if (usepercentiles) outf << line2 << endl;
        }
    }
}

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

MapPage::MapPage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth)
{
} // MapPage::constructor

void MapPage::Show()
{
    if (!registry.GetDataPack().AnyMapping())
    {
        return;
    }

    PrintTitle();

    //The results of the mapping algorithm is stored in the data pack.
    for (long reg=0; reg<registry.GetDataPack().GetNRegions(); reg++)
    {
        const Region& region = registry.GetDataPack().GetRegion(reg);
        for (long mloc=0; mloc<region.GetNumMovingLoci(); mloc++)
        {
            PrintBlankLine();
            const Locus& locus = region.GetMovingLocus(mloc);
            switch (locus.GetAnalysisType())
            {
                case mloc_mapjump:
                    PrintWrapped("The analysis for this trait was performed by allowing the location of"
                                 " the trait marker to move from place to place as trees were created.");
                    break;
                case mloc_mapfloat:
                    PrintWrapped("This analysis for this trait was performed by collecting trees, then calculating"
                                 " the data likelihood of the trait marker at all allowed sites"
                                 " on those trees, and then averaging.");
                    break;
                case mloc_data:
                case mloc_partition:
                    assert(false);      //These loci should not be in the moving locus vector.
                    return;
            }
            PrintBlankLine();

            PrintWrapped("Mapping results for " + locus.GetName()
                         + " from the region \""
                         + region.GetRegionName() + "\".");
            long regoffset = region.GetSiteSpan().first;
            PrintWrapped(locus.ReportMappingInfo(regoffset));
            PrintBlankLine();
        }
    }
    PrintPageBreak();
}

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

ProfPage::ProfPage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth)
{
    colwidth = 10;
} // ProfPage::constructor

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

void ProfPage::Show()
{
    const ParamVector overallparams(true);
    RegionGammaInfo *pRegionGammaInfo = registry.GetRegionGammaInfo();

    if (overallparams.CheckCalcProfiles() == paramlist_NO) return;

    PrintTitle();
    PrintBlankLine();
    if (overallparams.size() > 1)
    {
        outf << "The first listed parameter is the parameter held constant."
             << endl;
        ++current_length;
        PrintBlankLine();
    }

    PrintLineOf('=');
    outf << MakeCentered("Overall Profile Tables") << endl;
    ++current_length;
    PrintLineOf('=');
    PrintBlankLine();
    pagetitle.push_back("(overall profile tables)");

    DisplayParameters(overallparams,FLAGLONG); // print the overall profiles

    if (pRegionGammaInfo)
        pRegionGammaInfo->Deactivate(); // to avoid attempting to print alpha in the
    // regional profiles; see regionalparams below

    unsigned long nregions = registry.GetDataPack().GetNRegions();
    if (verbosity == VERBOSE && nregions > 1)
    {
        const ParamVector regionalparams(true); // will not contain alpha
        PrintLineOf('=');
        outf << MakeCentered("Regional Profile Tables") << endl;
        ++current_length;
        PrintLineOf('=');
        PrintBlankLine();
        for(unsigned long region = 0; region < nregions; ++region)
        {
            string rname = registry.GetDataPack().GetRegion(region).GetRegionName();
            pagetitle[1] = "(regional profile tables for region " + rname + ")";
            PrintBlankLine();
            outf << "****** Estimates for region " << rname;
            outf << " ******" << endl;
            ++current_length;
            PrintBlankLine();
            DisplayParameters(regionalparams,region);
        }
    }

    // If desired, at this point one could return the registry's "RegionGammaInfo"
    // to its former state by calling pRegionGammaInfo->Activate().

    PrintPageBreak();

} // ProfPage::Show

// "region" is FLAGLONG if the overall profiles are being asked for
// "region" is the region number, numbering from zero, of the region
//    being profiled.
void ProfPage::DisplayParameters(const ParamVector& params, long region)
{
    const ForceSummary& forcesummary = registry.GetForceSummary();

    string regionname = "Overall";
    if (region != FLAGLONG)
    {
        regionname = registry.GetDataPack().GetRegion(region).GetRegionName();
    }

    unsigned long pindex, nparams = params.size();
    for(pindex = 0; pindex < nparams; ++pindex)
    {
        const Parameter param = params[pindex];
        proftype ptype = param.GetProfileType();
        if (!param.IsValidParameter() || ptype == profile_NONE) continue;

        StringVec2d forcetable;
        string line, explanation;
        if (profile_FIX == ptype)
        {
            if (m_bayesanalysis)
                explanation = "Fixed profile: Points shown are pre-determined multiples of MPE.\n";
            else
                explanation = "Fixed profile: Points shown are pre-determined multiples of MLE.\n";
        }
        else // profile_PERCENTILE == ptype
        {
            if (m_bayesanalysis)
                explanation = "Percentile profile: Points shown indicate credibility intervals.\n";
            else
                explanation = "Percentile profile: Points shown indicate approximate confidence intervals.\n";
        }
        if (current_length + 4 + 12 > pagelength)
        {
            PrintPageBreak();
            PrintTitle();
            PrintBlankLine();
        }
        line = regionname + ": " + param.GetName() + " (" + param.GetShortName() + ")";
        PrintTableTitle(line);
        PrintWrapped(explanation);
        //Set up the first three columns.
        StringVec1d column;
        string label;
        string mle;
        double mleval, mleperc = 0.0; //Set to zero to avoid compiler warning.
        DoubleVec1d modifiers;
        StringVec1d mod_strings;
        bool mixedmult = false;

        vector<vector<centilepair> > centiles;
        vector<centilepair> CIvec;

        if (region==FLAGLONG)
        {
            mleval = param.GetOverallMLE();
            centiles = param.GetOverallProfile();
            CIvec = param.GetOverallCIs();
        }
        else
        {
            mleval = param.GetMLE(region);
            centiles = param.GetProfiles(region);
            CIvec = param.GetCIs(region);
        }
        if (m_bayesanalysis)
        {
            mleperc = GetReverseCentile(CIvec, mleval);
        }
        //LS TEST
#if 0
        for (unsigned long i=0; i<CIvec.size(); i++)
            cout << CIvec[i].first << ", " << CIvec[i].second << endl;
#endif

        // The first column:  Percentile/Multiplier labels.
        if (ptype == profile_FIX)
        {
            label = "Multiplier";
        }
        else  {
            label = "Percentile";
        }
        mle = m_MLE;
        modifiers = forcesummary.GetModifiers(pindex); // FS gets 'em from ParamVector
        //we need the pindex modifier in case we're fixed and have growth.
        mod_strings = (VecElemToString(modifiers));
        if (ptype == profile_PERCENTILE)
        {
            for (unsigned long i=0; i<mod_strings.size(); ++i)
            {
                string fullnum = "0.000";
                fullnum.replace(0, mod_strings[i].size(),mod_strings[i]);
                mod_strings[i] = fullnum;
            }
            if (m_bayesanalysis)
            {
                double percval = GetReverseCentile(CIvec,mleval);
                // code to get the percentile as a string of form d.ddd
                char myBuffer[6];
                if (sprintf(myBuffer,"%04.3f",percval) != 5)
                {
                    assert(false);
                }
                mle = myBuffer;
                mle = mle + "-" + m_MLE;
            }
        }
        else  { //ptype == fixed, we've already assured ptype != none
            if ((param.IsForce(force_GROW)) && (ptype == profile_FIX) &&(verbosity != CONCISE) && (verbosity != NONE))
            {
                mod_strings = MakeGrowFixedColumn();
                mixedmult = true;
                if (m_bayesanalysis)
                    modifiers = MakeGrowFixedModifiers(mleval);
            }
            else if ((param.IsForce(force_LOGISTICSELECTION)) &&
                     (ptype == profile_FIX) &&(verbosity != CONCISE) && (verbosity != NONE))
            {
                mod_strings = MakeLogisticSelectionFixedColumn();
                mixedmult = true;
                if (m_bayesanalysis)
                    modifiers = MakeLogisticSelectionFixedModifiers(mleval);
            }
            else
            {
                for (unsigned long i=0; i<mod_strings.size(); ++i)
                {
                    mod_strings[i] = MakeJustified(mod_strings[i],-5);
                    if (m_bayesanalysis)
                        modifiers[i] = modifiers[i]*mleval;
                }
            }
        }
        column = MakeColumn(label, mle, mod_strings, mixedmult);
        if (mixedmult)
        {
            column[0] = MakeCentered("[Value] or", colwidth);
        }
        forcetable.push_back(column);

        //The second column:  The fixed parameter
        label = param.GetShortName();
        MakeItShorter(label, colwidth-2);
        mle = Pretty(mleval, colwidth-2);

        if (m_bayesanalysis && ptype==profile_FIX)
        {
            //We have to report both the likes and the percentiles.  Also,
            // our modifiers string holds the values, not the mults.

            column = MakeColumn(label, mle, VecElemToString(modifiers, colwidth-2), mixedmult);
            forcetable.push_back(column);

            //Now Make a third column of percentiles.
            label = "Percentiles";
            mle = Pretty(GetReverseCentile(CIvec, mleval), colwidth-2);
        }

        vector<bool> badLnLs(column.size(), false);
        bool bayesfixed = (m_bayesanalysis && ptype == profile_FIX);
        mod_strings = MakeModColFrom(CIvec, modifiers, badLnLs, bayesfixed);

        column = MakeColumn(label, mle, mod_strings, mixedmult);
        forcetable.push_back(column);

        //The third column:  Log likelihoods
        if (m_bayesanalysis)
        {
            label = "Point Prob";
            if (ptype==profile_FIX)
                TradeValsForPercs(modifiers, CIvec);
        }
        else
            label = "Ln(L)";

        if (region==FLAGLONG)
        {
            mle = Pretty(forcesummary.GetOverallLlikeMle(), colwidth-2);
            CIvec = param.GetOverallPriorLikes();
        }
        else
        {
            mle = Pretty(forcesummary.GetLlikeMle(region), colwidth-2);
            CIvec = param.GetPriorLikes(region);
        }
        if (m_bayesanalysis)
        {
            mle = Pretty(GetCentile(CIvec, mleperc));
        }

        mod_strings = MakeLnLColFrom(CIvec,modifiers,badLnLs);
        column = MakeColumn(label, mle, mod_strings, mixedmult);
        forcetable.push_back(column);

        //Print a table of just the log likelihoods
        if (m_bayesanalysis)
        {
            PrintWrapped("Point Probabilities:");
        }
        else
        {
            PrintWrapped("Log Likelihoods:");
        }
        if (mixedmult)
        {
            line = "Values used for growth are a combination of multiples of the "
                + m_MLE + " and fixed values.  Fixed values are shown in [brackets].";
            PrintWrapped(line);
        }
        PrintBlankLine();
        StringVec2d sortedtable = SortTable(forcetable, 2, (mixedmult ? 2 : 1));
        StringVec3d wrappedtable = WrapTable(sortedtable);
        PrintTable(wrappedtable);

        //Now take back off the log likelihoods line.
        forcetable.pop_back();

        long nvariable_params = params.NumVariableParameters();
        //Now loop through all the rest of the parameters.
        if ((nvariable_params > 1) && !m_bayesanalysis)
        {
            line = "Best fit parameters with " + param.GetShortName()
                + " held constant:";
            PrintWrapped(line);
            PrintBlankLine();
            for(unsigned long p2index = 0; p2index < nparams; ++p2index)
            {
                const Parameter floatparam = params[p2index];
                if (p2index == pindex || !floatparam.IsVariable()) continue;
                //Note:  comment out the IsVariable condition above if you wish to
                // display those parameters in profiling.
                label = floatparam.GetShortName();
                MakeItShorter(label, colwidth-1);
                if (region == FLAGLONG)
                    mle = Pretty(params[p2index].GetOverallMLE(), colwidth-2);
                else
                    mle = Pretty(params[p2index].GetMLE(region), colwidth-2);

                mod_strings = MakeModColFrom(centiles[p2index], modifiers, badLnLs);

                column = MakeColumn(label, mle, mod_strings, mixedmult);
                forcetable.push_back(column);
            }

            sortedtable = SortTable(forcetable, 2, (mixedmult ? 2 : 1));
            wrappedtable = WrapTable(sortedtable);
            PrintTable(wrappedtable);
            PrintBlankLine();
        }
    }
} // ProfPage::DisplayParameters

StringVec1d ProfPage::MakeColumn(const string& name, const string& mle,
                                 const StringVec1d mod_strings, bool mixedmult)
{
    StringVec1d column;
    if (mixedmult)
        column.push_back("");
    column.push_back(name);
    unsigned long length = mod_strings.size();
    for (unsigned long i=0; i<length; ++i)
    {
        column.push_back(mod_strings[i]);
        if (i==length/2-1)
            column.push_back(mle);
    }
    return column;
}

//A special case to end all special cases.  This code should trigger if and
// only if we're trying to make a long (not concise) list of growth modifiers
// with fixed profiling.  The reason for this is that this case does some
// multiplying and some set-value-to-X'ing.
StringVec1d ProfPage::MakeGrowFixedColumn()
{
    StringVec1d gmult  = VecElemToString(vecconst::growthmultipliers);
    StringVec1d gfixed = VecElemToString(vecconst::growthfixed);
    StringVec1d combined;
    for (unsigned long i=0; i<gmult.size(); ++i)
        combined.push_back(MakeJustified(gmult[i], -7));
    for (unsigned long i=0; i<gfixed.size(); ++i)
        combined.push_back(MakeJustified(("[" + gfixed[i]) + "]", -7));
    return combined;
}

DoubleVec1d ProfPage::MakeGrowFixedModifiers(double mle)
{
    DoubleVec1d gmult  = vecconst::growthmultipliers;
    DoubleVec1d gfixed = vecconst::growthfixed;
    DoubleVec1d combined;
    for (unsigned long i=0; i<gmult.size(); ++i)
        combined.push_back(gmult[i]*mle);
    for (unsigned long i=0; i<gfixed.size(); ++i)
        combined.push_back(gfixed[i]);
    return combined;
}

// Ha-ha!  A new special case, perfectly analogous to the previous one,
// just a different force.
StringVec1d ProfPage::MakeLogisticSelectionFixedColumn()
{
    StringVec1d lsmult  = VecElemToString(vecconst::logisticselectionmultipliers);
    StringVec1d lsfixed = VecElemToString(vecconst::logisticselectionfixed);
    StringVec1d combined;
    for (unsigned long i=0; i<lsmult.size(); ++i)
        combined.push_back(MakeJustified(lsmult[i], -7));
    for (unsigned long i=0; i<lsfixed.size(); ++i)
        combined.push_back(MakeJustified(("[" + lsfixed[i]) + "]", -7));
    return combined;
}

DoubleVec1d ProfPage::MakeLogisticSelectionFixedModifiers(double mle)
{
    DoubleVec1d lsmult  = vecconst::logisticselectionmultipliers;
    DoubleVec1d lsfixed = vecconst::logisticselectionfixed;
    DoubleVec1d combined;
    for (unsigned long i=0; i<lsmult.size(); ++i)
        combined.push_back(lsmult[i]*mle);
    for (unsigned long i=0; i<lsfixed.size(); ++i)
        combined.push_back(lsfixed[i]);
    return combined;
}

StringVec1d ProfPage::MakeModColFrom(const vector<centilepair>& numbers,
                                     const DoubleVec1d modifiers,
                                     vector<bool> badLnLs,
                                     bool bayesfixed)
{
    if (badLnLs.size() < modifiers.size())
        badLnLs.assign(modifiers.size(), false);
    StringVec1d line;
    for(unsigned long perc = 0; perc < modifiers.size(); ++perc)
        if (numbers.empty()) line.push_back(NOVAL);
        else
        {
            if (badLnLs[perc])
                line.push_back(ERRVAL);
            else
            {
                if (!bayesfixed)
                {
                    line.push_back(Pretty(GetCentile(numbers,modifiers[perc]),colwidth-2));
                }
                else
                {
                    line.push_back(Pretty(GetReverseCentile(numbers,modifiers[perc]),colwidth-2));
                }
            }
        }
    return line;

} // ProfPage::MakeModColFrom

StringVec1d ProfPage::MakeLnLColFrom(const vector<centilepair>& numbers,
                                     const DoubleVec1d modifiers,
                                     vector<bool>& badLnLs)
{
    StringVec1d line;
    for(unsigned long perc = 0; perc < modifiers.size(); ++perc)
        if (numbers.empty()) line.push_back(NOVAL);
        else
        {
            double val = GetCentile(numbers, modifiers[perc]);
            if (val == -DBL_BIG)
            {
                line.push_back(ERRVAL);
                badLnLs[perc] = true;
            }
            else
            {
                line.push_back(Pretty(val,colwidth-2));
            }
        }

    return line;

} // ProfPage::MakeModColFrom

void ProfPage::TradeValsForPercs(DoubleVec1d& modifiers, vector<centilepair> CIvec)
{
    for (unsigned long i=0; i<modifiers.size(); i++)
    {
        vector<centilepair>::const_iterator cent;
        bool found=false;
        for(cent = CIvec.begin(); cent != CIvec.end() && !found; ++cent)
        {
            if (CloseEnough(cent->second, modifiers[i]))
            {
                modifiers[i] = cent->first;
                found = true;
            }
        }
        assert (found); //Didn't find a needed number to trade out.
    }
}

StringVec3d ProfPage::WrapTable(StringVec2d& forcetable)
{
    StringVec3d wrappedtable;
    StringVec2d subtable;
    unsigned long maxcols = (pagewidth-2) / (colwidth+1);
    //The -2 is for an extra couple spaces between the fixed parameter and the
    //rest of the parameters.
    subtable.push_back(forcetable[0]);
    subtable.push_back(forcetable[1]);
    unsigned long nextwrap = maxcols;
    for (unsigned long column=2; column < forcetable.size(); ++column)
    {
        if (column==nextwrap)
        {
            nextwrap += maxcols-2;
            wrappedtable.push_back(subtable);
            subtable.clear();
            subtable.push_back(forcetable[0]);
            subtable.push_back(forcetable[1]);
        }
        subtable.push_back(forcetable[column]);
    }
    wrappedtable.push_back(subtable);
    return wrappedtable;

} // ProfPage::WrapTable

void ProfPage::PrintTable(StringVec3d& wrappedtable)
{
    for (unsigned long table=0; table<wrappedtable.size(); ++table)
    {
        if (wrappedtable[table][0].size() + current_length > pagelength)
        {
            PrintPageBreak();
            PrintTitle();
            PrintBlankLine();
        }
        // outf << current_length;
        for (unsigned long row=0; row<wrappedtable[table][0].size(); ++row)
        {
            StringVec1d line = GetOneRow(wrappedtable[table], row);
            PrintProfileLine(line);
        }
        PrintBlankLine();
    }
} // ProfPage::PrintTable

void ProfPage::PrintProfileLine(StringVec1d& line)
{
    for (unsigned long col=0; col<line.size(); ++col)
    {
        if (col==2)
            outf << " | ";
        outf << MakeCentered(line[col], colwidth);
    }
    outf << endl;
    ++current_length;
}

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

UsrPage::UsrPage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth)
{

    if (registry.GetForceSummary().CheckForce(force_DIVERGENCE)) {
      npops = registry.GetDataPack().GetNPartitionsByForceType(force_DIVMIG);
    } else {
      npops = registry.GetDataPack().GetNPartitionsByForceType(force_MIG);
    }

} // UsrPage::UsrPage

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

void UsrPage::Show()
{
    const ForceSummary& forcesum = registry.GetForceSummary();
    vector<string>::iterator sit;

    PrintTitle();
    PrintBlankLine();
    string line = "Force specific options";

    if (verbosity == NORMAL || verbosity == VERBOSE)
    {
        PrintTableTitle(line);

        PrintWrapped("Starting Parameters:");
        PrintBlankLine();

        const ForceVec forces = forcesum.GetAllForces();
        ForceVec::const_iterator fit;
        for(fit = forces.begin(); fit != forces.end(); ++fit)
        {
            vector<string> str = Linewrap((*fit)->MakeStartParamReport(),pagewidth);

            for(sit = str.begin(); sit != str.end(); ++sit)
                outf << sit->c_str() << endl;
            PrintBlankLine();
        }
        PrintBlankLine();
        PrintBlankLine();
        // Write a report on priors used
        if (m_bayesanalysis)
        {
            PrintWrapped("Priors for Parameters:");
            PrintBlankLine();
            const ForceVec forces = registry.GetForceSummary().GetAllForces();
            ForceVec::const_iterator fit;
            for(fit = forces.begin(); fit != forces.end(); ++fit)
            {
                PriorReport preport(**fit);
                preport.WriteTo(outf);
            }
            PrintBlankLine();
            PrintBlankLine();
        }

    }

    // WARNING v2--Custom force matrix reports go here!!

    const ChainParameters& chainparams = registry.GetChainParameters();
    line.assign("Search Strategy");
    PrintTableTitle(line);
    line = "Type of analysis:  ";
    if (registry.GetChainParameters().IsBayesian())
    {
        line += "Bayesian";
    }
    else
    {
        line += "Likelihood";
    }
    PrintWrapped(line);
    PrintBlankLine();

    line = "Number of replicates:  ";
    line += ToString(registry.GetChainParameters().GetNReps());
    PrintWrapped(line);
    PrintBlankLine();

    PrintWrapped("Markov Chain Parameters:");
    m_colwidths.clear();
    m_colwidths.push_back(18);
    m_colwidths.push_back(16);
    m_colwidths.push_back(16);
    vector<string> strvec;
    strvec.push_back(" ");
    strvec.push_back("Initial");
    strvec.push_back("Final");
    PrintSimpleRow(m_colwidths,strvec);
    strvec.clear();
    strvec.push_back("Number of Chains");
    strvec.push_back(ToString(chainparams.GetNChains(0)));
    strvec.push_back(ToString(chainparams.GetNChains(1)));
    PrintSimpleRow(m_colwidths,strvec);
    strvec.clear();
    strvec.push_back("Trees Sampled");
    strvec.push_back(ToString(chainparams.GetNSamples(0)));
    strvec.push_back(ToString(chainparams.GetNSamples(1)));
    PrintSimpleRow(m_colwidths,strvec);
    strvec.clear();
    strvec.push_back("Sampling Increment");
    strvec.push_back(ToString(chainparams.GetInterval(0)));
    strvec.push_back(ToString(chainparams.GetInterval(1)));
    PrintSimpleRow(m_colwidths,strvec);
    strvec.clear();
    strvec.push_back("Trees Discarded");
    strvec.push_back(ToString(chainparams.GetNDiscard(0)));
    strvec.push_back(ToString(chainparams.GetNDiscard(1)));
    PrintSimpleRow(m_colwidths,strvec);
    PrintBlankLine();

    // all remaining entries are 2 column entries with this format
    m_colwidths.clear();
    m_colwidths.push_back(30);
    m_colwidths.push_back(-40);

    DoubleVec1d chtemps = chainparams.GetAllTemperatures();
    long temp, ntemps = chtemps.size();
    if (ntemps > 1)
    {
        line.erase();
        for (temp = 0; temp < ntemps; ++temp)
        {
            line += " "+ToString(chtemps[temp]);
            if (temp == ntemps-1) continue;
            if (temp == ntemps-2)
            {
                line += " and";
                continue;
            }
            line += ',';
        }
        PrintTwoCol(m_colwidths,"Chain Temperatures",line);
    }

#if 0  // WARNING warning -- No haplotype model interface stuff yet
    line.assign(iobag.GetHapmodel());
    if (line != "none")
    {
        PrintTwoCol(m_colwidths,"Haplotype rearrangement",line);
    }
#endif

    const UserParameters& userparams = registry.GetUserParameters();
    line = ToString(userparams.GetRandomSeed());
    PrintTwoCol(m_colwidths,"Random number seed",line);
    PrintBlankLine();
    PrintBlankLine();

    if (verbosity != CONCISE && (verbosity != NONE))
    {
        PrintTableTitle("File options");
        line.assign("Read data from file:");
        PrintTwoCol(m_colwidths,line,userparams.GetDataFileName());
        if (userparams.GetReadSumFile())
        {
            line.assign("Read summary file:");
            PrintTwoCol(m_colwidths, line, userparams.GetTreeSumInFileName());
        }
        if (userparams.GetWriteSumFile())
        {
            line.assign("Wrote summary file:");
            PrintTwoCol(m_colwidths, line, userparams.GetTreeSumOutFileName());
        }
        StringVec1d curvefilenames = userparams.GetCurveFileNames();
        if (curvefilenames.size() > 0)
        {
            //LS NOTE: not GetWriteCurveFiles() because if not bayesian, that
            // parameter is meaningless.  This version is much safer.
            line.assign("Wrote to curve file(s):");
            for (size_t i=0; i<curvefilenames.size(); i++)
            {
                PrintTwoCol(m_colwidths, line, curvefilenames[i]);
                line.assign("");
            }
        }
        StringVec1d mapfilenames = userparams.GetMapFileNames();
        if (mapfilenames.size() > 0)
        {
            line.assign("Wrote to mapping file(s):");
            for (size_t i=0; i<mapfilenames.size(); i++)
            {
                PrintTwoCol(m_colwidths, line, mapfilenames[i]);
                line.assign("");
            }
        }
        StringVec1d profilenames = userparams.GetProfileNames();
        if (profilenames.size() > 0)
        {
            line.assign("Wrote to profile file(s):");
            for (size_t i=0; i<profilenames.size(); i++)
            {
                PrintTwoCol(m_colwidths, line, profilenames[i]);
                line.assign("");
            }
        }
        set<string> tracefilenames = userparams.GetTraceFileNames();
        if (tracefilenames.size() > 0)
        {
            line.assign("Wrote to Tracer file(s):");
            for (set<string>::iterator tname = tracefilenames.begin();
                 tname != tracefilenames.end(); tname++)
            {
                PrintTwoCol(m_colwidths, line, *tname);
                line.assign("");
            }
        }
        set<string> newicktreefilenames = userparams.GetNewickTreeFileNames();
        if (newicktreefilenames.size() > 0)
        {
            line.assign("Wrote to Newick Tree file(s):");
            for (set<string>::iterator nname = newicktreefilenames.begin();
                 nname != newicktreefilenames.end(); nname++)
            {
                PrintTwoCol(m_colwidths, line, *nname);
                line.assign("");
            }
        }
        PrintBlankLine();
        PrintBlankLine();

        PrintTableTitle("Output summary options");
        //   line = ((userparams.GetEchoData()) ? "Yes" : "No");
        //   PrintTwoCol(m_colwidths,"Echo data?",line);

        ParamVector paramvec(false);
#if 0 //WARNING no plotting of likelihood curves yet, 2001/11/12
        paramlistcondition test = paramvec.CheckCalcPProfiles();
        line = (test==paramlist_NO ? "No" : "Yes");
        PrintTwoCol(m_colwidths, "Plot likelihood curves?", line);
#endif
        paramlistcondition test = paramvec.CheckCalcProfiles();
        line = (test==paramlist_NO ? "None" : "Yes");
        PrintTwoCol(m_colwidths, "Calculate profile likelihoods?", line);
    }

    PrintPageBreak();

} // UsrPage::Show

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

DataPage::DataPage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth),
      table1(1), table2(2)
{
    nxparts = registry.GetDataPack().GetNCrossPartitions();

} // DataPage::DataPage

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

vector<string> DataPage::SetupColhdr(long whichtable)
{
    long totlength=0;
    string line;
    vector<string> hdr;
    vector<long>::iterator lit;

    for(lit = m_colwidths.begin(); lit != m_colwidths.end(); ++lit)
        totlength += *lit;

    if (whichtable == table1)
    {
        line.clear();
        line.append(m_colwidths[0]+m_colwidths[1],' ');
        line.append(MakeCentered("Datatype",m_colwidths[2]));
        line.append(MakeCentered("MuRate",m_colwidths[3]));
        line += "\n";
        hdr.push_back(line);
    }

    if (whichtable == table2)
    {
        line = MakeJustified("Population",-1*m_colwidths[0]);
        line += MakeCentered("Variable",m_colwidths[1]);
        line += MakeCentered("Relative",m_colwidths[2]);
        line += MakeCentered("Relative",m_colwidths[2]);
        line += MakeCentered("Pairwise",m_colwidths[3]);
        line += MakeCentered("Sample",m_colwidths[4]);
        line += "\n";
        hdr.push_back(line);
        line = "   ";
        line += MakeJustified("Region",-1*(m_colwidths[0]-3));
        line += MakeCentered("markers",m_colwidths[1]);
        line += MakeCentered("Ne",m_colwidths[2]);
        line += MakeCentered("rec rate",m_colwidths[2]);
        line += MakeCentered("theta",m_colwidths[3]);
        line += MakeCentered("size",m_colwidths[4]);
        line += "\n";
        hdr.push_back(line);
    }

    return(hdr);

} // DataPage::SetupColhdr

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

StringVec2d DataPage::SetupRowhdr(long whichtable)
{
    const DataPack& datap = registry.GetDataPack();
    long nreg = datap.GetNRegions(), totlength = 0;
    string line;
    vector<string> vecline;
    StringVec2d hdr;
    vector<long>::iterator lit;

    for(lit = m_colwidths.begin(); lit != m_colwidths.end(); ++lit)
        totlength += *lit;

    if (whichtable == table1)
    {
        long reg;
        for(reg = 0; reg < nreg; ++reg)
        {
            const Region& region = datap.GetRegion(reg);
            vecline.clear();
            char spc(' ');
            string line(region.GetRegionName());
            if (m_colwidths[0]-line.size() > 0)
                line.append(m_colwidths[0]-line.size()-1,spc);
            line += "|";
            vecline.push_back(line);
            line.assign(m_colwidths[0]-1,spc);
            line += "|";
            vecline.insert(vecline.end(),region.GetNumAllLoci(),line);
            hdr.push_back(vecline);
        }
    }

    if (whichtable == table2)
    {
        vector<string> xpartn = datap.GetAllCrossPartitionNames();
        long xpart, reg;
        for(xpart = 0; xpart < nxparts; ++xpart)
        {
            vecline.clear();
            line = indexToKey(xpart)+" "+xpartn[xpart];
            vecline.push_back(line);
            for(reg = 0; reg < nreg; ++reg)
            {
                line = indexToKey(reg)+" "+datap.GetRegion(reg).GetRegionName();
                vecline.push_back(line);
            }
            hdr.push_back(vecline);
        }
    }

    return(hdr);

} // DataPage::SetupRowhdr

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

StringVec3d DataPage::SetupInnards(long whichtable)
{
    string tmp;
    StringVec1d line;
    StringVec2d line2;
    StringVec3d innards;
    const DataPack& datapack = registry.GetDataPack();

    if (whichtable == table1)           //Multi-locus
    {
        LongVec1d locreg = datapack.GetNumAllLociPerRegion();
        StringVec2d locn = datapack.GetAllLociNames();
        StringVec1d regnames = datapack.GetAllRegionNames();
        StringVec2d locd = datapack.GetAllLociDataTypes();
        StringVec2d locm = datapack.GetAllLociMuRates();
        long reg, nreg = datapack.GetNRegions();
        for(reg = 0; reg < nreg; ++reg)
        {
            line2.push_back(line);
            long loc;
            for(loc = 0; loc < locreg[reg]; ++loc)
            {
                line.push_back(locn[reg][loc]+" |");
                line.push_back(locd[reg][loc]);
                line.push_back(locm[reg][loc]);
                line2.push_back(line);
                line.clear();
            }
            innards.push_back(line2);
            line2.clear();
        }
    }

    if (whichtable == table2)
    {
        long xpart, reg, nreg = datapack.GetNRegions();
        DoubleVec1d popsizescalars(datapack.GetRegionalPopSizeScalars());
        for(xpart = 0; xpart < nxparts; ++xpart)
        {
            line2.clear();
            line2.push_back(line);
            for(reg = 0; reg < nreg; ++reg)
            {
                const Region& regs = datapack.GetRegion(reg);
                deque<bool> isCalculated;
                DoubleVec1d qthetas(nxparts);
                DoubleVec1d muratios = regs.GetMuRatios();
                ThetaWattersonRegion(datapack,regs,popsizescalars[reg],muratios,
                                     qthetas,isCalculated);
                line.push_back(ToString((regs.CalcNVariableMarkers())[xpart]));
                line.push_back(ToString(popsizescalars[reg]));
                line.push_back(/* region relative rec-rate goes here */ "1.0");
                line.push_back(ToString(qthetas[xpart]));
                line.push_back(ToString(regs.GetNXTips(xpart)));
                // line.push_back(ToString(regs.GetNIndividuals()));
                line2.push_back(line);
                line.clear();
            }
            innards.push_back(line2);
        }
    }

    return(innards);

} // DataPage::SetupInnards

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

void DataPage::Show()
{
    const DataPack& datapack = registry.GetDataPack();
    long nreg = datapack.GetNRegions();

    PrintTitle();
    PrintBlankLine();

    if (verbosity != CONCISE && (verbosity != NONE))
    {
        m_colwidths.clear();
        m_colwidths.push_back(22);
        m_colwidths.push_back(38);
        long npops(0L);
        if (registry.GetForceSummary().CheckForce(force_DIVERGENCE)){
          npops = datapack.GetNPartitionsByForceType(force_DIVMIG);
        } else {
          npops = datapack.GetNPartitionsByForceType(force_MIG);
        }
        if (!npops) ++npops;
        string line = ToString(npops);
        PrintTwoCol(m_colwidths,"Number of populations:",line);
        line = ToString(nreg);
        PrintTwoCol(m_colwidths,"Number of regions:",line);
        m_colwidths.clear();
        m_colwidths.push_back(42);
        m_colwidths.push_back(18);
        line = "Total number of samples in all regions";
        string line1 = ToString(datapack.GetNTips());
        PrintTwoCol(m_colwidths,line,line1);
        PrintBlankLine();
        PrintBlankLine();

        if (datapack.HasMultiLocus())
        {
            PrintTableTitle("Linked-segments by region");
            m_colwidths.clear();
            m_colwidths.push_back(18);  // for region name + 1
            m_colwidths.push_back(11);  // for locus name + 1
            m_colwidths.push_back(10);  // for datatype
            m_colwidths.push_back(18);  // for murate
            StringVec1d colhdr = SetupColhdr(table1);
            StringVec2d rowhdr = SetupRowhdr(table1);
            StringVec3d innards = SetupInnards(table1);
            PrintTable(colhdr,rowhdr,m_colwidths,innards);
            PrintBlankLine();
            PrintBlankLine();
        }

        PrintTableTitle("Region summary");
        m_colwidths.clear();
        m_colwidths.push_back(15);      // col for names
        m_colwidths.push_back(10);      // col for # variable markers
        m_colwidths.push_back(10);      // col for relative population size
        m_colwidths.push_back(10);      // col for relative recombination rate
        m_colwidths.push_back(10);      // col for simple theta value
        m_colwidths.push_back(10);      // col for # of individuals
        StringVec1d colhdr = SetupColhdr(table2);
        StringVec2d rowhdr = SetupRowhdr(table2);
        StringVec3d innards = SetupInnards(table2);
        PrintTable(colhdr,rowhdr,m_colwidths,innards);
        PrintBlankLine();
        PrintBlankLine();

        PrintTableTitle("Summary of Data Model Parameters");
        PrintBlankLine();
        PrintLineOf('-');
        long reg;
        for(reg = 0; reg < nreg; ++reg)
        {
            const Region& region = datapack.GetRegion(reg);
            StringVec2d dataModelsReports = region.CreateAllDataModelReports();
            for (StringVec2d::iterator oneReport = dataModelsReports.begin();
                 oneReport != dataModelsReports.end(); oneReport++)
            {
                PrintBlankLine();
                for (StringVec1d::iterator oneLine = (*oneReport).begin();
                     oneLine != (*oneReport).end(); oneLine++)
                {
                    outf << (*oneLine).c_str() << endl;
                }
                PrintLineOf('-');
            }
        }
    }

    if (verbosity == VERBOSE)
    {
        PrintBlankLine();
        outf << "Input Genetic Data" << endl;
        long reg, nregs = datapack.GetNRegions();
        for(reg = 0; reg < nregs; ++reg)
        {
            PrintLineOf('-');
            const Region& region = datapack.GetRegion(reg);
            outf << "   For the " << region.GetRegionName() << " region" << endl;
            PrintLineOf('-');
            StringVec2d dataecho(region.GetMarkerDataWithLabels());
            unsigned long loc, nloc(region.GetNumAllLoci());
            assert(nloc == dataecho.size());
            for(loc = 0; loc < nloc; ++loc)
            {
                StringVec1d lines(LinewrapCopy(dataecho[loc], 10));
                StringVec1d::const_iterator line;
                for(line = lines.begin(); line != lines.end(); ++line)
                    outf << *line << endl;
                PrintBlankLine();
            }
        }
    }

    PrintPageBreak();

} // DataPage::Show

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

RunPage::RunPage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth)
{

} // RunPage::RunPage

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

void RunPage::Show()
{
    PrintTitle();
    PrintBlankLine();
    string msg = "\"Accepted\" is the observed rate at which any change to the proposal trees ";
    if ((registry.GetChainParameters().GetAllTemperatures()).size() > 1)
    {
        msg += "in the coldest chain ";
    }
    msg += "was accepted.";
    PrintWrapped(msg);

    const RunReport& runreport = registry.GetRunReport();
    StringVec1d messages = runreport.GetMessages();
    KeyToLongMap keyFrequencies = runreport.GetKeyFrequencies();
    LongToKeyMap keyIndices = runreport.GetKeyIndices();

    pagewidth = 80;
    for (long strindex = 0; strindex<static_cast<long>(messages.size()); strindex++)
    {
        PrintWrapped(messages[strindex]);
        LongToKeyMapiter keyindex = keyIndices.find(strindex);
        if (keyindex != keyIndices.end())
        {
            //This is a 'ReportOnce' message.  Tell the user how often it got called later.
            OnceKey key = keyindex->second;
            long freq = keyFrequencies.find(key)->second;
            string msg = "[Note:  the conditions that triggered the above message happened a total of "
                + ToString(freq) + " time(s) in this run of LAMARC.]";
            PrintWrapped(msg);
            PrintBlankLine();
        }
    }

    PrintPageBreak();

} // RunPage::Show

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

LikePage::LikePage(ofstream& pf, long pglngth, long pgwdth)
    : ReportPage(pf,pglngth,pgwdth)
{

} // LikePage::LikePage

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

vector<string> LikePage::MakeInnards(const DoubleVec2d& likes)
{
    typedef pair<double, string> sympair;

    long x, y;
    double maxlike = likes[0][0];

    // find highest value in table
    for (x = 0; x < (long) likes.size(); ++x)
    {
        for (y = 0; y < (long) likes[x].size(); ++y)
        {
            if (likes[x][y] > maxlike) maxlike = likes[x][y];
        }
    }

    // establish critical values
    long i;
    vector<sympair> critvalues;

    critvalues.push_back(sympair(maxlike, string("X")));
    critvalues.push_back(sympair(maxlike - DF, string("*")));
    critvalues.push_back(sympair(maxlike - 2*DF, string("+")));
    critvalues.push_back(sympair(maxlike - 3*DF, string("-")));
    critvalues.push_back(sympair(NEG_MAX, string(" ")));

    // fill up table with symbols

    StringVec1d innards;
    string line;

    for (x = 0; x < (long) likes.size(); ++x)
    {
        for (y = 0; y < (long) likes[x].size(); ++y)
        {
            for (i = 0; i < (long) critvalues.size(); ++i)
            {
                if (likes[x][y] >= critvalues[i].first)
                {
                    line += critvalues[i].second;
                    break;
                }
            }
        }
        innards.push_back(line);
        line.erase();
    }

    return(innards);

} // LikePage::MakeInnards

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

StringVec1d LikePage::MakeLikePlot(const StringVec1d& innards, const
                                   Parameter& paramX, const Parameter& paramY, long breaks)
{
    StringVec1d plot;

#if 0
    // not till V2
    string line;
    long v, x, y;
    long linelength = 4 + paramX.plotpoints;

    // make figure legplotend
    // header lines
    line = "          X Axis    Y Axis";
    plot.push_back(Pretty(line, linelength));
    line = Pretty("    Tick",10);
    line += Pretty(paramX.name, 10);
    line += Pretty(paramY.name, 10);
    plot.push_back(Pretty(line, linelength));

    line = "          ";
    if (paramX.style == log_ten) line += "(log10)   ";
    else line += "(linear)  ";

    if (paramY.style == log_ten) line += "(log10)   ";
    else line += "(linear)  ";
    plot.push_back(Pretty(line, linelength));

    // value lines
    double val;
    long xvals = paramX.plotpoints/breaks;
    long yvals = paramY.plotpoints/breaks;
    long maxvals;
    if (xvals > yvals) maxvals = xvals;
    else maxvals = yvals;

    for (v = 0; v < maxvals; ++v)
    {
        line = Pretty(v+1L);

        if (v <= xvals)                 // print x values
        {
            val = paramX.plotstart +
                (double)v * (paramX.plotend - paramX.plotstart) / (double) paramX.plotpoints;
            if (paramX.style == log_ten) val = pow(10.0, val);
            line += "  " + Pretty(val);
        }
        else
            line += "          ";

        if (v <= yvals)                 // print y values
        {
            val = paramY.plotstart +
                (double)v * (paramY.plotend - paramY.plotstart) / (double) paramY.plotpoints;
            if (paramY.style == log_ten) val = pow(10.0, val);
            line += "  " + Pretty(val);
        }
        else
            line += "          ";

        plot.push_back(Pretty(line, linelength));
    }

    line.erase();

    // make actual figure
    plot.push_back(MakeBorder(paramX.plotpoints));
    for (x = 0; x < (long) innards.size(); ++x)
    {
        if (Divides(x+1, breaks)) line += indexToKey(breaks/paramY.plotpoints - x) + " +";
        else line += "  |";
        for (y = 0; y < (long) innards[x].size(); ++y)
        {
            line += innards[x][y];
        }
        if (Divides(x+1, breaks)) line += "+";
        else line += "|";
        plot.push_back(line);
        line.erase();
    }
    plot.push_back(MakeBorder(paramX.plotpoints));
    line.erase();
    for (x = 0; x < paramX.plotpoints; ++x)
    {
        if (Divides(x+1, breaks)) line += indexToKey(x);
        else line += " ";
    }
    plot.push_back(line);
#endif
    return(plot);

}  // LikePage::MakeLikePlot

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

bool LikePage::Divides(long x, long y)
{
    return (x/y * y == x);
} // LikePage::Divides

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

string LikePage::MakeBorder(long points, long breaks)
{

    string line;
    long i;
    line += "+";                            // left border
    for (i = 0; i < points; ++i)
    {
        if (Divides(i+1, breaks)) line += "+";
        else line += "-";
    }

    line += "+";                            // right border
    return(line);
} // LikePage::MakeBorder

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

DoubleVec2d LikePage::AddGraphs(const DoubleVec2d& a, const
                                DoubleVec2d& b)
{
    // Find highest value in either graph
    assert(a.size() == b.size());
    assert(a[0].size() == b[0].size()); // can't add graphs of different sizes

    double maxlike = a[0][0];
    long i, j;
    for (i = 0; i < (long) a.size(); ++i)
    {
        for (j = 0; j < (long) a[0].size(); ++j)
        {
            if (a[i][j] > maxlike) maxlike = a[i][j];
            if (b[i][j] > maxlike) maxlike = b[i][j];
        }
    }

    DoubleVec2d newgraph;
    DoubleVec1d newline;
    double aval, bval, newval = 0.0;

    // Normalize all values to the maximum
    for (i = 0; i < (long) a.size(); ++i)
    {
        for (j = 0; j < (long) a.size(); ++j)
        {
            aval = a[i][j] - maxlike;
            bval = b[i][j] - maxlike;
            if (aval > EXPMIN) newval += exp(aval);
            if (bval > EXPMIN) newval += exp(bval);
            if (newval != 0) newline.push_back(log(newval));
            else newline.push_back(EXPMIN);
            newval = 0.0;
        }
        newgraph.push_back(newline);
        newline.clear();
    }
    return(newgraph);

} // LikePage::AddGraphs

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

bool LikePage::EmptyPlot(PlotStruct& plot)
{

    return(plot.plane.empty());

} // LikePage::EmptyPlot

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

void LikePage::Show()
{

#if 0    // DEBUG !!  not till V3
    PrintTitle();
    PrintBlankLine();

    ParamVector parameters(false);

    // DEBUG vector<PlotStruct> plots = registry.GetPostoutPack().GetPriorgraphs();

    ParamVector::iterator pit;

    for(pit = parameters.begin(); pit != parameters.end(); ++pit)
    {
        if (!(pit->IsValidParameter())) continue;

        DoubleVec2d data = pit->plane;
        Parameter& xaxis = *(pit->xaxis);
        Parameter& yaxis = *(pit->yaxis);
        bool twoplots = false;
        StringVec1d innards = MakeInnards(data);
        StringVec1d plot = MakeLikePlot(innards, xaxis, yaxis);
        StringVec1d plot2;

        ++pit;
        if (pit != plots.end())
        {
            data = pit->plane;
            innards = MakeInnards(data);
            plot2 = MakeLikePlot(innards, xaxis, yaxis);
            twoplots = true;
        }

        if (twoplots)
        {
            long line;
            for (line = 0; line < (long)plot.size(); ++line)
                cout << plot[line] << "  " << plot2[line] << endl;
        }
        else
        {
            long line;
            for (line = 0; line < (long)plot.size(); ++line)
                cout << plot[line] << endl;
        }
    }

    PrintPageBreak();

#endif // 0

} // LikePage::Show

//____________________________________________________________________________________
