// $Id: region.cpp,v 1.94 2018/01/03 21:32:58 mkkuhner Exp $

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

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

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

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

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#include <cassert>
#include <fstream>                      // for Region::WriteFlucFile
#include <functional>                   // for Region::CountNBases()
#include <iostream>                     // jmdbg
#include <numeric>                      // for std::accumulate

#include "argtree.h"
#include "branch.h"
#include "collector.h"
#include "constants.h"
#include "datatype.h"
#include "dlmodel.h"
#include "errhandling.h"
#include "force.h"                      // for Region::CreateTree() for recombination && localpartitionforce combos
#include "mathx.h"
#include "region.h"
#include "registry.h"
#include "runreport.h"
#include "stringx.h"                    // for MakeTag()/MakeCloseTag() in Region::ToXML()
#include "toxml.h"                      // for SampleXML/IndividualXML/PopulationXML in Region::MakePopXML()
#include "tree.h"
#include "ui_constants.h"               // for uiconst::GLOBAL_ID
#include "ui_vars_forces.h"             // for getLegalForce
#include "xml_strings.h"                // for Region::ToXML()

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

using namespace std;

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

bool Region::RecombinationCanBeEstimated() const
{
    long loc;
    long nvar(0);
    // we only have primitives to get a list by xpart; we just sum it
    for(loc = 0; loc < GetNloci(); ++loc) {
       LongVec1d nvarmarkers = m_loci[loc].CalcNVariableMarkers();
       nvar += std::accumulate(nvarmarkers.begin(),nvarmarkers.end(),0L);
    }
    if (nvar > 1) return true;
    return false;
}

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

bool Region::MultiSampleIndividuals() const
{
    return m_loci[0].MultiSampleIndividuals();
}

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

Individual& Region::GetIndividual(long n)
{
    assert(0 <= n && n < static_cast<long>(m_individuals.size()));
    return m_individuals[n];
} // GetIndividual non-const

bool Region::ValidLocus(long locus) const
{
    if (locus == uiconst::GLOBAL_ID) return true;
    return locus >= 0 && locus < static_cast<long>(m_loci.size());
}

bool Region::ValidMovingLocus(long locus) const
{
    return locus >= 0 && locus < static_cast<long>(m_movingloci.size());
}

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

void Region::AddLocus(bool movable, string name)
{
    Locus locus(m_loci.size(), movable, name);
    m_loci.push_back(locus);
} // AddLocus

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

void Region::InitializeRegionalMapPositionsUsingGlobalMapPositions()
{
    vector<long> gmaps;
    vector<Locus>::iterator locus;
    // first do the normal loci
    for(locus = m_loci.begin(); locus != m_loci.end(); ++locus)
    {
        gmaps.push_back(locus->GetGlobalMapPosition());
    }
    long regionstart(*min_element(gmaps.begin(),gmaps.end()));

    for(locus = m_loci.begin(); locus != m_loci.end(); ++locus)
    {
        long newregionalpos(locus->GetGlobalMapPosition());
        newregionalpos -= regionstart;
        locus->SetRegionalMapPosition(newregionalpos);
    }

    // now do the moving loci
    if (!m_movingloci.empty())          // don't try if there are none!
    {
        gmaps.clear();
        for(locus = m_movingloci.begin(); locus != m_movingloci.end(); ++locus)
        {
            gmaps.push_back(locus->GetGlobalMapPosition());
        }
        regionstart = (*min_element(gmaps.begin(),gmaps.end()));

        for(locus = m_movingloci.begin(); locus != m_movingloci.end(); ++locus)
        {
            long newregionalpos(locus->GetGlobalMapPosition());
            newregionalpos -= regionstart;
            locus->SetRegionalMapPosition(newregionalpos);
        }
    }

}

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

void Region::SetupAndMoveAllLoci()
{
    for (unsigned long ind=0; ind<m_individuals.size(); ind++)
    {
        m_individuals[ind].RandomizeAllHaplotypes();
    }

    MakeAllMovableLociMove();

    // Set up the correspondence between regional and global positions
    // (This must be done before Setup, since it's needed to make
    // a healthy DLCalculator object).
    InitializeRegionalMapPositionsUsingGlobalMapPositions();

    // Setup the non-moving loci
    for (unsigned long loc = 0; loc < m_loci.size(); ++loc)
    {
        m_loci[loc].Setup(m_individuals);
    }

    // Setup the moving loci
    for (unsigned long mloc=0; mloc<m_movingloci.size(); ++mloc)
    {
        m_movingloci[mloc].Setup(m_individuals);
        m_movingloci[mloc].SetIndex(mloc);
    }

}

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

void Region::MakeAllMovableLociMove()
{
    for (vector<Locus>::iterator locus = m_loci.begin();
         locus != m_loci.end();)
    {
        if (locus->IsMovable())
        {
            m_movingloci.push_back(*locus);
            locus = m_loci.erase(locus);
        }
        else
        {
            ++locus;
        }
    }
}

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

void Region::RevertMovingLoci()
{
    for (vector<Locus>::iterator locus = m_movingloci.begin(); locus != m_movingloci.end(); ++locus)
    {
        m_loci.push_back(*locus);
    }
    m_movingloci.clear();
}

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

void Region::SetNmarkers(long locus, long n)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetNmarkers(n);
} // Region::SetNmarkers

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

void Region::SetGlobalMapPosition(long locus, long n)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetGlobalMapPosition(n);
} // Region::SetGlobalMapPosition

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

void Region::SetName(long locus, string name)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetName(name);
} // Region::SetName

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

void Region::SetOffset(long locus, long n)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetOffset(n);
} // Region::SetOffset

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

void Region::SetPositions(long locus, const LongVec1d& pos)
{
    assert(ValidLocus(locus));
    unsigned long index;
    for(index = 0; index < pos.size(); index++)
    {
        if(pos[index] < 0)
        {
            data_error e("Marker location must be >= 0");
            throw e;
        }
    }
    LongVec1d newpos(pos);
    sort(newpos.begin(), newpos.end());
    LongVec1d::iterator it = unique(newpos.begin(), newpos.end());
    if (it != newpos.end())
    {
        // unique removed some elements, so they were not all unique!
        data_error e("Duplicate locations in marker location list");
        throw e;
    }

    m_loci[locus].SetPositions(pos);
} // Region::SetPositions

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

void Region::SetPositions(long locus)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetPositions();
} // Region::SetPositions default form

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

void Region::SetNsites(long locus, long n)
{
    assert(ValidLocus(locus));
    if(n < 1)
    {
        assert(false);
        data_error e("Number of sites in region must be > 0");
        throw e;
    }
    m_loci[locus].SetNsites(n);
} // Region::SetNsites

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

void Region::SetDataType(long locus, const DataType_ptr dt)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetDataType(dt);

} // SetDataType

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

void Region::SetTipData(long locus, const TipData& td)
{
    assert(ValidLocus(locus));
    m_loci[locus].SetTipData(td);
} // SetTipData

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

void Region::SetPhaseMarkers(long indnum, LongVec2d& phasesites)
{
    assert(static_cast<size_t>(indnum) < m_individuals.size());
    LongVec2d allphasemarkers;
    for (size_t locus = 0; locus < phasesites.size(); ++locus)
    {
        LongVec1d phasemarkers;
        for (size_t sitenum = 0; sitenum<phasesites[locus].size(); ++sitenum)
        {
            long site = phasesites[locus][sitenum];
            long marker = m_loci[locus].SiteToMarker(site);
            if (marker==FLAGLONG)
            {
                throw data_error("Site "
                                 + ToString(site + GetOffset(locus))
                                 + " in locus "
                                 + ToString(locus+1)
                                 + " does not have a marker associated with it,"
                                 " and may therefore not be set 'phase unknown'.");
            }
            phasemarkers.push_back(marker);
        }
        allphasemarkers.push_back(phasemarkers);
    }

    m_individuals[indnum].SetPhaseMarkers(allphasemarkers);
    m_individuals[indnum].SetPhaseSites(phasesites);
}

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

LongVec1d Region::CalcNVariableMarkers() const
{
    LongVec1d total(m_loci[0].CalcNVariableMarkers());
    vector<Locus>::const_iterator loc;
    for(loc = m_loci.begin(), ++loc; loc != m_loci.end(); ++loc)
    {
        transform(total.begin(),total.end(),
                  loc->CalcNVariableMarkers().begin(),total.begin(),
                  plus<long>());
    }
    return total;

} // Region::CalcNVariableMarkers

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

bool Region::HasLocus(string lname) const
{
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (m_loci[locus].GetName() == lname) return true;
    }
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        if (m_movingloci[locus].GetName() == lname) return true;
    }
    return false;
}

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

long Region::GetNumAllLoci() const
{
    return (m_loci.size() + m_movingloci.size());
}

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

long Region::GetNumFixedLoci() const
{
    long numFixed = 0;
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if ( ! (m_loci[locus].IsMovable())) ++numFixed;
    }
    return numFixed;
}

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

long Region::GetLocusIndex(string lname) const
{
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (m_loci[locus].GetName() == lname) return locus;
    }
    if (HasLocus(lname))
    {
        assert(false);
        throw implementation_error("GetLocusIndex should only be used in phase one.");
    }
    return FLAGLONG;
}

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

const Locus& Region::GetLocus(string lname) const
{
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        if (m_movingloci[locus].GetName() == lname) return m_movingloci[locus];
    }
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (m_loci[locus].GetName() == lname) return m_loci[locus];
    }
    throw implementation_error("GetLocus(name) called on a nonexistent locus.");
}

//------------------------------------------------------------------------------------
// Note that this number includes sites within all Loci in the region plus sites in all inter-locus
// areas, for which recombination must be modeled even though we have no data for these sites.
//
// RSGFIXUP:  Range::s_numRegionSites is computed here.  Seems to be same value as Tree::m_totalSites.
// Either merge the two variables or guaranteed they track each other (or test with ASSERT that they do).

long Region::GetNumSites() const
{
    //LS DEBUG MAPPING  This assumes that there is no space to the right of the right-most, non-moving locus.
    long nsites = 0;

    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (!m_loci[locus].IsMovable())
        {
            nsites = max(nsites, m_loci[locus].GetRegionalMapPosition() + m_loci[locus].GetNsites());
        }
    }
    return nsites;
}

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

rangepair Region::GetSiteSpan() const
{
    //We want the labels of the first and last included site.
    long first = m_loci[0].GetGlobalMapPosition();
    long last = first + m_loci[0].GetNsites() - 1;

    for (size_t locus = 1; locus < m_loci.size(); ++locus)
    {
        if (!m_loci[locus].IsMovable())
        {
            long position1 = m_loci[locus].GetGlobalMapPosition();
            long position2 = position1 + m_loci[locus].GetNsites();
            first = min(first, position1);
            last = max(last, position2);
        }
    }
    return make_pair(first, last);
}

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

bool Region::HasUserTree() const
{
    if (m_usertree->Exists())
    {
        return true;
    }
    else if (m_argedges.size() > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
} // HasUserTree

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

void Region::MakeUserTree(Tree* treetips)
{
    if (m_usertree->Exists())
    {
        m_usertree->ToLamarcTree(*treetips);
    }
    else if (m_argedges.size() > 0)
    {
        ARGTree* argtree = new ARGTree();
        argtree->ToLamarcTree(*treetips, m_argedges);
    }
} // Region::MakeUserTree

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

StringVec1d Region::ToXML(unsigned long nspaces) const
{
    StringVec1d xmllines;
    string line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_REGION,GetRegionName()),nspaces);
    xmllines.push_back(line);

    nspaces += INDENT_DEPTH;
    vector<Locus>::const_iterator loc;
    for(loc = m_loci.begin(); loc != m_loci.end(); ++loc)
    {
        StringVec1d dlmodelxml(loc->GetDataModel()->ToXML(nspaces));
        xmllines.insert(xmllines.end(),dlmodelxml.begin(),dlmodelxml.end());
    }

    string mytag = MakeTag(xmlstr::XML_TAG_EFFECTIVE_POPSIZE);
    line = MakeIndent(mytag,nspaces) + ToString(m_effpopsize) +
        MakeCloseTag(mytag);
    xmllines.push_back(line);

#if 0  // JDEBUG -- if (user_specified_tree)
    line = MakeIndent(MakeTag(xmlstr::XML_TAG_TREE),nspaces)
        xmllines.push_back(line);
    nspaces += INDENT_DEPTH;
    NewickConverter nc;
    string outtree = nc.LamarcTreeToNewickString(a_lamarc_tree_reference);
    OR
        NewickTree nt(stored_user_newick_tree);
    string outtree = nt.Need_To_Write_Dump_Of_Newick_Format_Function();
    line = MakeIndent(outtree,nspaces);
    xmllines.push_back(line);
    nspaces -= INDENT_DEPTH;
    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_TREE),nspaces)
        xmllines.push_back(line);
#endif // 0

    line = MakeIndent(MakeTag(xmlstr::XML_TAG_SPACING),nspaces);
    xmllines.push_back(line);
    nspaces += INDENT_DEPTH;

    unsigned long locus, nloci(m_loci.size());
    for(locus = 0; locus < nloci; ++locus)
    {
        line = MakeIndent(MakeTagWithName(xmlstr::XML_TAG_BLOCK,m_loci[locus].GetName()),nspaces);
        xmllines.push_back(line);
        nspaces += INDENT_DEPTH;
        mytag = MakeTag(xmlstr::XML_TAG_MAP_POSITION);
        line = MakeIndent(mytag,nspaces) + ToString(GetGlobalMapPosition(locus)) + MakeCloseTag(mytag);
        xmllines.push_back(line);
        mytag = MakeTag(xmlstr::XML_TAG_LENGTH);
        line = MakeIndent(mytag,nspaces) + ToString(GetLocusNsites(locus)) + MakeCloseTag(mytag);
        xmllines.push_back(line);
        if (!m_loci[locus].IsUsingDefaultLocations())
        {
            mytag = MakeTag(xmlstr::XML_TAG_LOCATIONS);
            line = MakeIndent(mytag,nspaces) + ToString(GetUserMarkerLocations(locus))
                + MakeCloseTag(mytag);
            xmllines.push_back(line);
        }
        mytag = MakeTag(xmlstr::XML_TAG_OFFSET);
        line = MakeIndent(mytag,nspaces) + ToString(GetOffset(locus)) + MakeCloseTag(mytag);
        xmllines.push_back(line);
        nspaces -= INDENT_DEPTH;
        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_BLOCK),nspaces);
        xmllines.push_back(line);
    }

    nspaces -= INDENT_DEPTH;
    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_SPACING),nspaces);
    xmllines.push_back(line);

    StringVec1d traits(MakeTraitsXML(nspaces));
    xmllines.insert(xmllines.end(), traits.begin(), traits.end());

    nspaces -= INDENT_DEPTH;
    //cerr << "in Region::ToXML calling Region::ToXML" << endl; //jmdbg
    StringVec1d populations(MakePopXML(nspaces));
    xmllines.insert(xmllines.end(),populations.begin(),populations.end());

    line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_REGION),nspaces);
    xmllines.push_back(line);

    return xmllines;
} // Region::ToXML

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

StringVec1d Region::MakeTraitsXML(unsigned long nspaces) const
{
    StringVec1d finalxml;
    StringVec1d xmllines;

    long regoffset = GetSiteSpan().first;
    for (size_t mlnum = 0; mlnum < m_movingloci.size(); ++mlnum)
    {
        nspaces += INDENT_DEPTH;
        StringVec1d locxml = m_movingloci[mlnum].MakeTraitXML(nspaces, regoffset);
        nspaces -= INDENT_DEPTH;
        xmllines.insert(xmllines.end(), locxml.begin(), locxml.end());
    }
    if (xmllines.size() > 0)
    {
        string line = MakeIndent(MakeTag(xmlstr::XML_TAG_TRAITS),nspaces);
        finalxml.push_back(line);
        finalxml.insert(finalxml.end(), xmllines.begin(), xmllines.end());
        line = MakeIndent(MakeCloseTag(xmlstr::XML_TAG_TRAITS),nspaces);
        finalxml.push_back(line);
    }
    return finalxml;
}

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

StringVec1d Region::MakePopXML(unsigned long nspaces) const
{
    StringVec1d xmllines;

    vector<PopulationXML> pops(MakeVectorOfPopulationXML());
    //cerr << "in Region::MakePopXML pops.size: " << pops.size() << endl; //jmdbg

    vector<PopulationXML>::iterator population;
    for(population = pops.begin(); population != pops.end(); ++population)
    {
        StringVec1d pxml(population->ToXML(nspaces));
        xmllines.insert(xmllines.end(),pxml.begin(),pxml.end());
    }

    return xmllines;

} // MakePopXML

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

DoubleVec1d Region::GetMuRatios() const
{
    DoubleVec1d ratios;
    vector<Locus>::const_iterator loc = m_loci.begin();
    for( ; loc != m_loci.end(); ++loc)
    {
        ratios.push_back(loc->GetDataModel()->GetRelMuRate());
    }
    return ratios;
} // Region::GetMuRatios

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

long Region::GetNXTips(long xpart) const
{
    map<force_type,string> tipid = registry.GetDataPack().GetTipId(xpart);
    return m_loci[0].GetNTips(tipid);
}

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

DoubleVec1d Region::CountNBases() const
{
    DoubleVec1d nbases(BASES, 0L);
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
    {
        DoubleVec1d nnucs(locit->CountNNucleotides());
        transform(nbases.begin(),nbases.end(),nnucs.begin(),
                  nbases.begin(),plus<double>());
    }

    return nbases;

} // Region::CountNBases

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

void Region::AddUniqueNamesTo(set<string>& popnames) const
{
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
    {
        locit->AddUniqueNamesTo(popnames);
    }

} // Region::AddUniqueNamesTo

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

long Region::GetLocusNsites(long locus) const
{
    assert(ValidLocus(locus));
    return m_loci[locus].GetNsites();
} // Region::GetLocusNsites

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

StringVec1d Region::GetAllLociNames() const
{
    StringVec1d lnames;
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
        lnames.push_back(locit->GetName());
    for(locit = m_movingloci.begin(); locit != m_movingloci.end(); ++locit)
        lnames.push_back(locit->GetName());

    return lnames;

} // Region::GetAllLociNames

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

StringVec1d Region::GetAllLociDataTypes() const
{
    StringVec1d ltypes;
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
        ltypes.push_back(ToString(locit->GetDataType()));
    for(locit = m_movingloci.begin(); locit != m_movingloci.end(); ++locit)
        ltypes.push_back(ToString(locit->GetDataType()));

    return ltypes;

} // Region::GetAllLociDataTypes

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

StringVec1d Region::GetAllLociMuRates() const
{
    StringVec1d lrates;
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
        lrates.push_back(ToString(locit->GetMuRate()));
    for(locit = m_movingloci.begin(); locit != m_movingloci.end(); ++locit)
        lrates.push_back(ToString(locit->GetMuRate()));

    return lrates;

} // Region::GetAllLociMuRates

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

// MDEBUG This routine is on the wrong class, and pokes way too
// much into Tree's innards.  If we replace use of the Prototype
// pattern with Factory we can get a lot of this into the
// Tree ctor instead, where it belongs

Tree* Region::CreateTree()
{
    // Create the Tree.
    Tree* tree = registry.GetProtoTree().MakeStump();

    // This function cannot be made const because of these two calls:
    tree->SetLocusVec(&m_loci);
    tree->SetSnpPanelFlag(GetSnpPanel());

    const ForceSummary& fs(registry.GetForceSummary());
    if (fs.CheckForce(force_REC))
    {
        // If recombination is on, the tree needs the moving locus vector (even if it's empty).
        dynamic_cast<RecTree*>(tree)->SetMovingLocusVec(&m_movingloci);
    }

    // We now rely on this happening before tip construction, which it does.
    rangeset traitmutationsites;
    if (fs.GetNLocalPartitionForces() != 0)
    {
        // Fill up the tree's m_alwaysactive rangeset.
        const ForceVec lpforces(fs.GetLocalPartitionForces());
        ForceVec::const_iterator lpforce;
        for(lpforce = lpforces.begin(); lpforce != lpforces.end(); ++lpforce)
        {
            const LocalPartitionForce* lpf(dynamic_cast<const LocalPartitionForce*>(*lpforce));
            assert(lpf);
            rangepair localsite(lpf->GetLocalSite(), lpf->GetLocalSite() + 1);
            traitmutationsites = AddPairToRange(localsite, traitmutationsites);
        }
    }

    // Rewrite of individual setup code to use the region's already existing
    // individual vector, Region::individuals -- Jon 2001/11/08
    //
    // NB:  This code modified *copies* of the individual and tipdata
    // information, leaving the originals untouched.  I have changed
    // it to modify the originals.  Mary 2002/8/20
    //
    // NBB: Cannot modify the originals!  CreateTree() is called once for
    // each different temperature, and needs a copy for the branch pointers.

    IndVec individuals = m_individuals;

    // We used to pair sequences into individuals if it could be done, for
    // haplotyping purposes.  However, this can be accomplished easily by
    // the converter, and it was messing stuff up with the new haplotyping
    // of traits code, so it's now gone!  Expunged!  Brutally eviscerated!

    long ntips = GetNTips();

    // Collect the initialized LocusCells for each tip.
    vector<vector<LocusCell> > tipcells(ntips);
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        vector<LocusCell> locuscells = m_loci[locus].GetTipCells();
        for (long tip = 0; tip < ntips; ++tip)
        {
            tipcells[tip].push_back(locuscells[tip]);
        }
    }
    vector<vector<LocusCell> > movingtipcells(ntips);
    for (size_t mlocus = 0; mlocus < m_movingloci.size(); ++mlocus)
    {
        vector<LocusCell> movinglocuscells = m_movingloci[mlocus].GetTipCells();
        for (long tip = 0; tip < ntips; ++tip)
        {
            movingtipcells[tip].push_back(movinglocuscells[tip]);
        }
    }

    // The locus' TipData is needed here (tip_id) only for non-
    // locus-specific attributes (label and membership).

    const vector<TipData>& tip_id = m_loci[0].GetTipData();

    for (long tip = 0; tip < ntips; ++tip)
    {
        // create the tip and put the cells into the tip
        TBranch_ptr pTip;
        if (m_snppanel)
        {
            // has panels
            pTip = tree->CreateTip(tip_id[tip], tipcells[tip], movingtipcells[tip], traitmutationsites, m_loci);
        }
        else
        {
            // no panels
            pTip = tree->CreateTip(tip_id[tip], tipcells[tip], movingtipcells[tip], traitmutationsites);
        }

        long ind = tip_id[tip].individual;
        if (ind == FLAGLONG)
        {
            // FLAGLONG means "not part of an individual"
            //LS NOTE:  You should never hit this since the XML reader requires
            // all samples to be included in an 'individual' tag.
            assert(false);
            continue;
        }
        IndVec::iterator indiv;
        for(indiv = individuals.begin(); indiv != individuals.end(); ++indiv)
        {
            if (indiv->GetId() == ind)
            {
                indiv->AddTip(pTip);
                break;
            }
        }
    }

    PruneSamePhaseUnknownSites(individuals);

    tree->SetIndividualsWithTips(individuals);

    tree->SetupAliases(m_loci);

    return tree;

} // Region::CreateTree

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

bool Region::AnyPhaseUnknownSites() const
{
    for (IndVec::const_iterator indiv = m_individuals.begin();
         indiv != m_individuals.end(); ++indiv)
    {
        if (indiv->AnyPhaseUnknownSites()) return true;
    }
    return false;
}

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

void Region::PruneSamePhaseUnknownSites(IndVec& indivs) const
{
    IndVec::iterator ind;
    for(ind = indivs.begin(); ind != indivs.end(); ++ind)
        ind->PruneSamePhaseUnknownSites();

} // Region::PruneSamePhaseUnknownSites

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

void Region::RemovePartitionFromLoci(force_type forcename)
{
    vector<Locus>::iterator locus;
    for(locus = m_loci.begin(); locus != m_loci.end(); ++locus)
        locus->RemovePartitionFromTipDatas(forcename);

} // Region::RemovePartitionFromLoci

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

void Region::CopyTipDataForLocus(const string& lname)
{
    long lnum = GetLocusIndex(lname);
    if (lnum == 0)
    {
        throw data_error("We do not currently support haplotype data as the only data about a region.  Haplotype"
                         " information should be used for mapping onto sequenced or otherwise known segments.");
    }
    m_loci[lnum].SetEmptyTipData(m_loci[0].GetTipData());
}

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

StringVec2d Region::GetMarkerDataWithLabels() const
{
    StringVec2d labeleddata;
    vector<Locus>::const_iterator locit;
    for(locit = m_loci.begin(); locit != m_loci.end(); ++locit)
        labeleddata.push_back(locit->GetMarkerDataWithLabels(m_individuals));
    for(locit = m_movingloci.begin(); locit != m_movingloci.end(); ++locit)
        labeleddata.push_back(locit->GetMarkerDataWithLabels(m_individuals));

    return labeleddata;

} // Region::GetMarkerDataWithLabels

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

bool Region::CanHaplotype() const
{
    // we can haplotype if there are individuals with multiple sequences and
    // if there are phase unknown sites.
    return (AnyPhaseUnknownSites() && MultiSampleIndividuals());
} // CanHaplotype

bool Region::AnyMapping() const
{
    return (m_movingloci.size() > 0);
}

bool Region::AnyJumpingAnalyses() const
{
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        if (m_movingloci[locus].GetAnalysisType() == mloc_mapjump) return true;
    }
    return false;
}

bool Region::AnySimulatedLoci() const
{
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (m_loci[locus].GetShouldSimulate()) return true;
    }
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        if (m_movingloci[locus].GetShouldSimulate()) return true;
    }
    return false;
}

bool Region::AnySNPDataWithDefaultLocations() const
{
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if ((m_loci[locus].GetDataType() == dtype_SNP) && (m_loci[locus].IsUsingDefaultLocations()))
        {
            return true;
        }
    }
    //Don't check the moving loci because that would just be weird.  Moving SNPs?  What?
    return false;
}

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

bool Region::IsValidRegion(string & errorString) const
{
    size_t i;
    vector<pair<long,long> > spans;

    // check validity of individual loci, and store their spans
    for (i = 0; i < m_loci.size(); ++i)
    {
        if (!m_loci[i].IsValidLocus(errorString)) return false;
        if (!m_loci[i].IsMovable())
        {
            spans.push_back(m_loci[i].GetGlobalScaleSiteSpan());
        }
    }

    // check that locus spans do not overlap
    sort(spans.begin(), spans.end());

    for (i = 1; i < spans.size(); ++i)
    {
        if (spans[i].first < spans[i-1].second)
        {
            errorString = "Overlapping segments in region " + m_regionname;
            return false;
        }
    }

    // validate individuals
    for (i = 0; i < m_individuals.size(); ++i)
    {
        if (!m_individuals[i].IsValidIndividual())
        {
            errorString = "Invalid individuals in region " + m_regionname;
            return false;
        }
    }
    return true;

} // Region::IsValidRegion

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

bool Region::IsDuplicateTipName(const string& newname) const
{
    size_t i;
    for(i = 0; i < m_loci.size(); ++i)
    {
        if (m_loci[i].IsDuplicateTipName(newname)) return true;
    }

    return false;

} // Region::IsDuplicateTipName

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

void Region::SaveMappingInfo(MapCollector* mapcoll)
{
    DoubleVec2d maps = mapcoll->GetMapSummary();
    assert(maps.size() == m_movingloci.size());

    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        m_movingloci[locus].SetRawMappingInfo(maps[locus]);
        ScaleToSumToOne(maps[locus]);
        m_movingloci[locus].SetMappingInfo(maps[locus]);
    }
    //LS NOTE SIM: If I want to resurrect the old code here that tried
    // to deal with simulating sites which we then left 'unsequenced' in the
    // middle of 'sequenced' sites, it was here as of 3/31/06.  But I deleted
    // it as part of our code cleanup, and it didn't really work anyway.
    //
    // Note that if it's added back in, it would also need to be used in the
    // other SaveMappingInfo function, below.
}

void Region::SaveMappingInfo(vector<MapCollector*> mapcolls, DoubleVec1d logweights)
{
    if (m_movingloci.size() == 0) return;
    DoubleVec1d weights = SafeExp(logweights);
    size_t numreps = mapcolls.size();
    assert(numreps > 0);
    DoubleVec2d averagedmaps = (*mapcolls[0]).GetMapSummary();
    for (size_t map=0; map<averagedmaps.size(); map++)
    {
        //multiply by the correct weight (for averaging)
        transform(averagedmaps[map].begin(),
                  averagedmaps[map].end(),
                  averagedmaps[map].begin(),
                  bind2nd(multiplies<double>(),weights[0]));
    }
    for (size_t rep=1; rep<numreps; rep++)
    {
        DoubleVec2d repmaps = (*mapcolls[rep]).GetMapSummary();
        for (size_t map=0; map<repmaps.size(); map++)
        {
            //Multiply by the appropriate weight
            transform(repmaps[map].begin(),
                      repmaps[map].end(),
                      repmaps[map].begin(),
                      bind2nd(multiplies<double>(),weights[rep]));
            //sum
            transform(repmaps[map].begin(),
                      repmaps[map].end(),
                      averagedmaps[map].begin(),
                      averagedmaps[map].begin(),
                      plus<double>());
        }
    }
    assert(averagedmaps.size() == m_movingloci.size());
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        m_movingloci[locus].SetRawMappingInfo(averagedmaps[locus]);
        ScaleToSumToOne(averagedmaps[locus]);
        m_movingloci[locus].SetMappingInfo(averagedmaps[locus]);
    }

}

void Region::ReportMappingInfo()
{
    long regoffset = GetSiteSpan().first;
    for (size_t locus = 0; locus < m_movingloci.size(); ++locus)
    {
        registry.GetRunReport().ReportNormal(m_movingloci[locus].ReportMappingInfo(regoffset));
    }
    for (size_t locus = 0; locus < m_loci.size(); ++locus)
    {
        if (m_loci[locus].GetShouldSimulate())
        {
            string msg = ToString(CountSites(m_loci[locus].CalculateCompleteVariableRange()))
                + " variable sites simulated in segment "
                + ToString(m_loci[locus].GetName()) + ", "
                + ToString(CountSites(m_loci[locus].CalculateVariableRange()))
                + " of which had a minor allele frequency of at least three.";
            registry.GetRunReport().ReportNormal(msg);

#if 0  // LS DEBUG SIM:  Reporting tool for the whole 'known/unknown' thing:
            msg = ToString(m_loci[locus].GetVariabilityOfUnknownRange()) +
                " variable sites in the 'unknown' section of the segment.";
            registry.GetRunReport().ReportNormal(msg);
#endif // 0
        }
    }
}

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

StringVec2d Region::CreateAllDataModelReports() const
{
    StringVec2d reports;
    for (size_t lnum = 0; lnum < m_loci.size(); ++lnum)
    {
        reports.push_back(m_loci[lnum].CreateDataModelReport(m_regionname));
    }
    for (size_t lnum = 0; lnum < m_movingloci.size(); ++lnum)
    {
        reports.push_back(m_movingloci[lnum].CreateDataModelReport(m_regionname));
    }
    return reports;
}

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

void Region::WriteAnyMapping() const
{
    for (size_t lnum = 0; lnum < m_movingloci.size(); ++lnum)
    {
        m_movingloci[lnum].WriteMapping(GetRegionName(), GetSiteSpan().first);
    }
}

//------------------------------------------------------------------------------------
// WriteFlucFile is used only when JSIM is defined (as of 3/30/06 --LS)

void Region::WriteFlucFile(const string& outfilename, bool onlyregion) const
{
    long locus(0L);

    ofstream ofile;
    if (onlyregion) ofile.open(outfilename.c_str(),ios::out | ios::trunc);
    else ofile.open(outfilename.c_str(),ios::out | ios::app);

    vector<TipData> tips(GetAllTipData(locus));

    if (onlyregion) ofile << 1 << endl;
    ofile << tips.size() << " " << GetNmarkers(locus) << endl;

    vector<TipData>::iterator tit;
    for(tit = tips.begin(); tit != tips.end(); ++tit)
    {
        string dlm("");
        ofile << MakeJustified(tit->label, 10L) << tit->GetFormattedData(dlm) << endl;
    }

    ofile.close();

} // Region::WriteFlucFile

//------------------------------------------------------------------------------------
//
// These various XML writing functions are used when JSIM is defined (and only
// then) --3/30/06, LS
//
// This simulation function does not handle spacing or multiple loci correctly

void Region::WritePopulationXMLFiles() const
{
    ofstream ofile;
    StringVec2d populations(MakeByPopXML(0L));

    long pop, npops(populations.size());
    for(pop = 0; pop < npops; ++pop)
    {
        string fname(GetRegionName());
        fname += "pop" + ToString(pop);
        ofile.open(fname.c_str(), ios::out | ios::trunc);
        StringVec1d::iterator plines;
        for(plines = populations[pop].begin(); plines != populations[pop].end();
            ++plines)
            ofile << *plines << endl;
        ofile.close();

        fname = GetRegionName() + "popall";
        ofile.open(fname.c_str(), ios::out | ios::app);
        for(plines = populations[pop].begin(), ++plines;
            plines != populations[pop].end(); ++plines)
            ofile << *plines << endl;
        ofile.close();
    }

}

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

void Region::WriteToXMLFileUsing(ofstream& ofile, StringVec1d& region_contents) const
{
    ofile << MakeTagWithName(xmlstr::XML_TAG_REGION,GetRegionName()) << endl;

    StringVec1d::iterator cline;
    for(cline = region_contents.begin(); cline != region_contents.end(); ++cline)
    {
        ofile << *cline << endl;
    }

    ofile << MakeCloseTag(xmlstr::XML_TAG_REGION) << endl;
}

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

void Region::WriteToXMLFileUsing(ofstream& ofile, StringVec2d& region_contents,
                                 bool produceonepop) const
{
    ofile << MakeTagWithName(xmlstr::XML_TAG_REGION,GetRegionName()) << endl;

    StringVec2d::iterator pop;
    for(pop = region_contents.begin(); pop != region_contents.end(); ++pop)
    {
        StringVec1d::iterator cline;
        for(cline = pop->begin(); cline != pop->end(); ++cline)
        {
            if (produceonepop)
            {
                // skip the population start xml tags, unless we're doing the
                // first population
                if (pop != region_contents.begin() && cline == pop->begin())
                {
                    continue;
                }
                // skip the end population tag, we'll manually add one
                if (*cline == pop->back()) // slow, compares strings, would like to
                {
                    continue;           // compare iterators, but --(pop->end())
                } // and similar may not be supported
            }
            ofile << *cline << endl;
        }
        if (produceonepop)
        {
            ofile << MakeCloseTag(xmlstr::XML_TAG_POPULATION) << endl;
        }
    }

    ofile << MakeCloseTag(xmlstr::XML_TAG_REGION) << endl;
}

//------------------------------------------------------------------------------------
// RSGNOTE:  If DIVMIG is on, must record more info (EPOCH times, which pop diverge, etc), than with MIG (and no DIV).
// (DIVMIG, DIVERGENCE, EPOCH) all travel together.
// MIG and (DIVMIG, DIVERGENCE, EPOCH) are mutually contradictory.  If MIG is present, none of other three can be.
// If any one of (DIVMIG, DIVERGENCE, EPOCH) is present, so must be the other two, and MIG cannot be.

vector<PopulationXML> Region::MakeVectorOfPopulationXML() const
{
    force_type migforce;
    unsigned long npops;

    // NB:  If neither force_MIG nor force_DIVMIG is present, this code goes through
    // defaults (in GetAllPartitionNames()) which result in a single population.

    if (registry.GetForceSummary().CheckForce(force_MIG))
    {
        migforce = force_MIG;
    }
    else
    {
        migforce = force_DIVMIG;
    }

    StringVec1d popnames(registry.GetDataPack().GetAllPartitionNames(migforce));
    vector<PopulationXML> pops;

    npops = popnames.size();
    if (migforce == force_DIVMIG)
    {                                   // We only want entries for modern populations.
        npops = (npops + 1) / 2;        // Truncation is deliberate.
    }

    for(size_t pindex = 0; pindex < npops; pindex++)
    {
        pops.push_back(PopulationXML(*this,popnames[pindex]));
    }

    return pops;

} // MakeVectorOfPopulationXML

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

StringVec2d Region::MakeByPopXML(unsigned long nspaces) const
{
    StringVec2d xmllines;

    vector<PopulationXML> pops(MakeVectorOfPopulationXML());

    vector<PopulationXML>::iterator population;
    for(population = pops.begin(); population != pops.end(); ++population)
    {
        StringVec1d pxml(population->ToXML(nspaces));
        xmllines.push_back(pxml);
    }

    return xmllines;

} // MakeByPopXML

//____________________________________________________________________________________
