// $Id: range.cpp,v 1.51 2018/01/03 21:33:04 mkkuhner Exp $

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

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

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

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

*/

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


#include <cassert>
#include <iostream>                     // For debugging.
#include <iomanip>                      // For setw().

#include "local_build.h"
#include "dynatracer.h"                 // Defines some debugging macros.

#include "errhandling.h"
#include "force.h"                      // For LocalPartitionForce combos.
#include "range.h"
#include "rangex.h"
#include "region.h"
#include "registry.h"                   // For random.bool.
#include "stringx.h"
#include "vectorx.h"                    // For ForceVec.

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

#ifdef ENABLE_REGION_DUMP
#include "datatype.h"                   // For the data_type enum.
#endif

using namespace std;


//------------------------------------------------------------------------------------
// Class BiglinkMapitem.
//------------------------------------------------------------------------------------

#ifdef RUN_BIGLINKS

BiglinkMapitem::BiglinkMapitem(long int lowerlink, long int upperlink, double linkweight)
    : m_lower_littlelink(lowerlink),
      m_upper_littlelink(upperlink),
      m_linkweight(linkweight)
{
    // Link arguments (first, second) refer to Littlelinks, and hence their type is SIGNED LONG INT.
    // We reserve UNSIGNED LONG INTs to refer to Biglink indices.
    // Intentionally blank; everything is accomplished in initialization list above.
} // "Real" constructor for BiglinkMapitem class.

#endif // RUN_BIGLINKS


//------------------------------------------------------------------------------------
// Class Range.
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// Setter, for resetting this protected static variable when starting processing of a new region.
// Static: Sets static variable but may be called before any Range objects are created.
// Needs to be here rather than in "range.h" as inline because of some include-file dependency.
//
// 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).

void Range::SetNumRegionSites(const Region & curregion)
{
    s_numRegionSites = curregion.GetNumSites();
}

//------------------------------------------------------------------------------------
// The "real" constructor for class Range.
// This single-argument constructor is declared EXPLICIT to avoid accidental implicit conversion.

Range::Range(long int nsites)
{
    // "s_numRegionSites" should change ONLY when we begin processing a new region,
    // and it is set then by Range::SetNumRegionSites() before we ever create a Range/RecRange.
    // So we have nothing to do here.  All we do is check argument consistency via ASSERT.
#if 1
    DebugAssert2(nsites == s_numRegionSites, nsites, s_numRegionSites);
#else // Equivalent to DebugAssert2 above, in case it is removed later.
    assert(nsites == s_numRegionSites);
#endif

} // "Real" constructor for Range class.

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

Range * Range::Clone() const
{
    return new Range(*this);
} // Range::Clone

//------------------------------------------------------------------------------------
// This copy constructor is callable only by Range::Clone.

Range::Range(const Range & src)
{
    // Intentionally blank; everything is accomplished in initialization list above.
} // Copy constructor for Range class.

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// A non-recombinant Range is defined only by the total number of sites ("s_numRegionSites" is its only
// member variable), and that value (the length of the sequence) is a fixed property of the input data,
// for a given Region (and all Ranges are valid only within a single Region at a time).
// In this sense, all non-recombinant Ranges are equal.

bool Range::operator==(const Range & other) const
{
    return true;
} // Range::operator==

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::UpdateCRange(const Range * const child1rangeptr, const Range * const child2rangeptr,
                         const rangeset & fcsites, bool dofc)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::UpdateCRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::UpdateRootRange(const Range * const child1rangeptr, const Range * const child2rangeptr,
                            const rangeset & fcsites, bool dofc)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::UpdateRootRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::UpdateOneLeggedCRange(const Range * const childrangeptr)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::UpdateOneLeggedCRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::UpdateOneLeggedRootRange(const Range * const childrangeptr)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::UpdateOneLeggedRootRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::UpdateMRange(const Range * const child1rangeptr)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::UpdateMRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Polymorphic - Used in recombinant case; should never be called (thus ASSERTs) in non-recombinant case.

void Range::UpdateRRange(const Range * const child1rangeptr, const rangeset & fcsites, bool dofc)
{
    assert(false);
} // Range::UpdateRRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::ClearNewTargetLinks()
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::ClearNewTargetLinks

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::SetOldInfoToCurrent()
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::SetOldInfoToCurrent()

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void Range::ResetOldTargetSites(const rangeset & fcsites)
{
    // Intentionally empty; nothing to do in non-recombinant case.
} // Range::ResetOldTargetSites

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Polymorphic - Used in recombinant case; should never be called (thus ASSERTs) in non-recombinant case.

bool Range::AreChildTargetSitesTransmitted(const Range * const childrangeptr, const rangeset & fcsites) const
{
    assert(false);                      // Why are we asking this in a non-recombinant case?
    return false;                       // To silence compiler warning.
} // Range::AreChildTargetSitesTransmitted

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Called only by debugging function; virtual (must work differently on recombinant and non-recombinant Ranges).

bool Range::SameAsChild(const Range * const childrangeptr) const
{
    return true;
} // Range::SameAsChild

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).
// Prints in "internal" units, not "user" units.

void Range::PrintLive() const
{
    cerr << "Live Sites:             " << ToString(MakeRangeset(0L, s_numRegionSites)) << endl;
} // Range::PrintLive

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).

void Range::PrintInfo() const
{
    cerr << "Range::PrintInfo() ..." << endl << endl;
    cerr << "Total number of Sites in Region:   " << s_numRegionSites << endl;
    cerr << "All Sites transmitted in non-recombinant Range." << endl;
    PrintLive();
    cerr << "No Current Target Links in a non-recombinant Range." << endl;
    cerr << "No New Target Links in a non-recombinant Range." << endl;
    cerr << "No Old Target Sites in a non-recombinant Range." << endl;
    cerr << "No Old Target Links in a non-recombinant Range." << endl << endl;
}

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).

void Range::PrintNewTargetLinks() const
{
    cerr << "There are no Newly-Targetable Links in a non-recombinant Range." << endl;
} // Range::PrintNewTargetLinks


//------------------------------------------------------------------------------------
// Class RecRange.
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// Builds the Biglink Vector Map from recombination-visible weighted-span Biglinks
// to inter-site Littlelinks.  Defined only in the Biglink implementation.
// Static member function (RecRange-only, but called before first object is created).

#ifdef RUN_BIGLINKS

void RecRange::BuildBiglinkMap(const Region & curregion)
{
    const long int numloci(curregion.GetNloci());
    const long int numtips(curregion.GetNTips());  // All loci in region have data for same number of tips.

#ifdef ENABLE_BIGLINKMAP_DUMP
    cerr << "Region Name (name of Region):   \"" << curregion.GetRegionName() << "\"" << endl;
    cerr << "Number of Sites in the Region:  " << curregion.GetNumSites() << endl;  // All sites, whether in a locus or between them.
    cerr << "Number of Loci in this Region:  " << numloci << endl;
    cerr << "Number of Tips for each Locus:  " << numtips << endl << endl;
#endif // ENABLE_BIGLINKMAP_DUMP

    // Initialize a Biglink Associative Set to an empty set.  This is used locally in this function to store
    // the lower index of a candidate Biglink.  After all loci have been scanned and all Biglink lowerbound
    // Littlelink indices entered, the items in this set are used to construct a vector (the Biglink Vector Map)
    // of BiglinkMapitem objects.  This object is stored as the value of a static member variable for use by rest
    // of program.  The map-construction is done anew for each region as each map is valid only per-region.
    //
    // Note that this ASSOCIATIVE map will store one entry for each SITE (either EVERY site for EMULATE_LITTLELINKS
    // mode or one VARIANT or DISEASE site for regular RUN_BIGLINKS non-emulation mode).  When the VECTOR Biglink Map
    // is constructed, the one-fewer entries in it are constructed from the successive differences in the values
    // stored into adjacent entries in this ASSOCIATIVE map.  Thus total number of LITTLELINKS will be one less than
    // the total number of SITES in the Region in one mode, or the total number of BIGLINKS will be one less than
    // the total number of VARIANT SITES plus DISEASE SITE(S) in the Region.
    BiglinkAssocSet biglinkAssocSet;
    BiglinkAssocSet::iterator setiter(biglinkAssocSet.end());

#ifdef EMULATE_LITTLELINKS
    //
    // To emulate Littlelinks, we must iterate over ALL SITES in the REGION, not over all MARKERS in the LOCUS.
    // We must allocate a Map of size the total number of Littlelinks (one less than total number of sites in Region)
    // and store ONE BIGLINK PER SLOT in this map, with trivial component values.
    const long int numsites(curregion.GetNumSites());
    for (long int siteindex = 0L; siteindex < numsites; ++siteindex)
    {
        // "Hint" form of insert(), using END iterator.  Old value of "setiter" used as hint; since within
        // a locus we are scanning in increasing site index value.  Updated "setiter" points to the site
        // index just inserted and will be used as "hint" for the next insertion.
        setiter = biglinkAssocSet.insert(setiter, siteindex);
    }
    //
#else // EMULATE_LITTLELINKS
    //
    // If disease site(s) are present, then include them; ie, treat them as variant sites in map construction.
    if (registry.GetForceSummary().CheckForce(force_DISEASE))
    {
        const ForceSummary & fs(registry.GetForceSummary());
        //
        if (fs.GetNLocalPartitionForces() > 0)
        {
#ifdef ENABLE_BIGLINKMAP_DUMP
            cerr << "BuildBiglinkMap: Disease Site(s) present ..." << endl;
#endif
            //
            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);            // Make sure pointer is non-NULL.
                const long int disease_site(lpf->GetLocalSite());
                biglinkAssocSet.insert(setiter, disease_site);
                //
#ifdef ENABLE_BIGLINKMAP_DUMP
                cerr << "Disease Site at index: " << disease_site << endl;
#endif
            }
            //
#ifdef ENABLE_BIGLINKMAP_DUMP
            cerr << endl;
#endif
        }
    }
    //
    for (long int locIdx = 0L; locIdx < numloci; ++locIdx)
    {
        const Locus & locus(curregion.GetLocus(locIdx));
        const long int nmarkers(locus.GetNmarkers());
        const LongVec1d markerVector(locus.GetMarkerLocations());
        const vector<TipData> tipdatavec(locus.GetTipData());
        const DataType_ptr datatype_ptr(locus.GetDataTypePtr());
        //
#ifdef ENABLE_BIGLINKMAP_DUMP
        const data_type datatype_enum(locus.GetDataType());
        bool found_marker = false;
        cerr << "Name of this Locus in Region:   " << locus.GetName() << endl;
        cerr << "Index of this Locus in Region:  " << locIdx << " (" << locIdx + 1 << " of " << numloci << ")" << endl;
        cerr << "DataType stored in this Locus:  " << datatype_enum << " (type " << ToString(datatype_enum) << ")" << endl;
        cerr << "Number of Sites in this Locus:  " << locus.GetNsites() << endl;
        cerr << "Number of Markers, this Locus:  " << nmarkers << endl;
        cerr << "RegionalMapPosition for Locus:  " << locus.GetRegionalMapPosition() << endl << endl;
#endif
        //
        // Compare each tip vector (>= 1) with the zeroth.  If any pair of markers compares "non-IsEquivalent()",
        // consider the site as VARIANT and compute the weighted distance (Littlelink count, for now) to the
        // preceding such variant site.  Note that this distance is meaningfull only BETWEEN variant sites;
        // the first and last variant sites have no Biglinks toward the "outside".  Note also that this
        // distance applies across ALL loci (ie, between last variant site in one locus and first in the
        // next locus).  These distances must be reflected back to actual sequence site indices (as stored
        // in vector returned by GetMarkerLocations) rather than in per-locus site indices.
        for (long int markerindex = 0L; markerindex < nmarkers; ++markerindex)
        {
            const long int markerLocationSiteIndex(markerVector[markerindex]);
            const string tipZeroMarker(tipdatavec[0].data[markerindex]);
            for (long int tipindex = 1; tipindex < numtips; ++tipindex)
            {
                // If we find a variant site, insert the site index into the set.  At this point we don't know
                // whether there are variant sites "above" or "below" this one, because there might be more loci
                // "above" or "below" the one being processed.  So we will scan the entire set when done.
                // If emulating Littlelinks, every Littlelink defines a Biglink.
                // For native Biglink operation, only variant sites (and disease) define Biglink boundaries.
                if ( ! datatype_ptr->IsEquivalent(tipZeroMarker, tipdatavec[tipindex].data[markerindex]) )
                {
#ifdef ENABLE_BIGLINKMAP_DUMP
                    // The "setw" manipulator works "per output field" and so must be re-applied each time needed.
                    found_marker = true;
                    cerr << "Compare: " << tipZeroMarker << ":" << tipdatavec[tipindex].data[markerindex]
                         << " at Tip " << setw(4) << tipindex << ", Tip_Array_Idx " << setw(4) << markerindex
                         << ", Region_Site_Idx " << setw(4) << markerLocationSiteIndex
                         << "; Inserting: " << setw(4) << markerLocationSiteIndex << endl;
#endif
                    //
                    // "Hint" form of insert(), using END iterator.  Old value of "setiter" used as hint; since within
                    // a locus we are scanning in increasing site index value.  Updated "setiter" points to the site
                    // index just inserted and will be used as "hint" for the next insertion.
                    setiter = biglinkAssocSet.insert(setiter, markerLocationSiteIndex);
                    break;              // Exit inner loop once the first variant marker pair is discovered.
                }
            }
        }
#ifdef ENABLE_BIGLINKMAP_DUMP
        if (found_marker)
        {
            cerr << endl;
        }
#endif
    }
    //
#endif // EMULATE_LITTLELINKS

    // (Re)initialize the Biglink Vector Map to an empty vector.  This must be done afresh for each Region processed.
    s_biglink_vectormap.clear();

    // Reserve capacity for exactly the known needed size, which is one less than the number of variant sites
    // (including disease site(s)) discovered, since Biglinks span BETWEEN adjacent variant (or disease) sites.
    unsigned long int biglink_map_size(biglinkAssocSet.size() - 1);
    //
    // The Map had better be of non-zero size.  This ASSERT will catch errors that can lead to an empty map.
    // If this happens, we need an upstream diagnostic in the user-input phase to detect and report the situation
    // rather than crashing here.  But this is temporary, for debugging.
    assert(biglink_map_size > 0UL);
    //
    // Assuming everything is OK, let's allocate and fill up the Map.
    s_biglink_vectormap.reserve(biglink_map_size);

#ifdef ENABLE_BIGLINKMAP_DUMP
    cerr << "Biglink Vector Map size:  " << biglink_map_size << endl;
    cerr << "Biglink Vector Map (<lower_index> : <upper_index> : <biglink_weight>) ..." << endl << endl;
#endif

    // Now sweep the set accumulating Biglink start points ("low" end of a section between two variant sites, whether
    // in same locus or between the "highest" variant in one locus and the "lowest" in the next "higher" locus.
    // Each Biglink interval is indexed in the Biglink Associative Set by the site index of its LOWER end.
    long int lower_index(FLAGLONG);
    long int mapindex(0L);
    for (BiglinkAssocSet::iterator setiter(biglinkAssocSet.begin()) ; setiter != biglinkAssocSet.end() ; ++setiter)
    {
        const long int upper_index(*setiter);
        // If there IS a variant site "before" the current entry (indicated by "lower_index" being non-negative,
        // that is not equal to FLAGLONG), record the distance.  There is no Biglink below the "start" end of the
        // first variant (or disease) site in the region.
        if (lower_index != FLAGLONG)
        {
            // Calculate range (Littlelink count) between variant sites.  This (times a recombination probability
            // of unity for now) provides the Link weight for this BiglinkMapitem object.
            const double linkweight(1.0 * static_cast<double>(upper_index - lower_index));

#ifdef ENABLE_BIGLINKMAP_DUMP
            cerr << "Map item " << setw(4) << mapindex << ": " << setw(4) << lower_index
                 << " : " << setw(4) << upper_index << " : " << linkweight << endl;
#endif

            // Now construct a BiglinkMapitem object and enter it (push_back) into the Biglink Vector Map.
            s_biglink_vectormap.push_back(BiglinkMapitem(lower_index, upper_index, linkweight));
            ++mapindex;                 // Increment index after map item written (not so on first iteration).
        }

        lower_index = upper_index;      // Update the index for the next iteration.
    }

#ifdef ENABLE_BIGLINKMAP_DUMP
    cerr << endl;
#endif

} // RecRange::BuildBiglinkMap

#endif // RUN_BIGLINKS

//------------------------------------------------------------------------------------
// The "real" constructor for class RecRange.

RecRange::RecRange(long int nsites, const rangeset & diseasesites, const rangeset & transmittedsites,
                   const rangeset & livesites, const linkrangeset & curtargetlinks, const linkrangeset & oldtargetlinks,
                   const rangeset & oldtargetsites, const rangeset & oldlivesites)
    : Range(nsites),
      m_liveSites(livesites),
      m_diseaseSites(diseasesites),
      m_transmittedSites(transmittedsites),
      m_curTargetLinks(curtargetlinks),
      m_curTargetLinkweight(AccumulatedLinkweight(curtargetlinks)),
      m_oldTargetSites(oldtargetsites),
      m_oldLiveSites(oldlivesites),
      m_oldTargetLinks(oldtargetlinks),
      m_newTargetLinks(RemoveRangeFromRange(oldtargetlinks, m_curTargetLinks)),
      m_newTargetLinkweight(AccumulatedLinkweight(m_newTargetLinks))
{
    // Intentionally blank; everything is accomplished in initialization list above.
} // "Real" constructor for RecRange class.

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

Range * RecRange::Clone() const
{
    return new RecRange(*this);
} // RecRange::Clone

//------------------------------------------------------------------------------------
// This copy constructor is callable only by RecRange::Clone.

RecRange::RecRange(const RecRange & src)
    : Range(src),
      m_liveSites(src.m_liveSites),
      m_diseaseSites(src.m_diseaseSites),
      m_transmittedSites(src.m_transmittedSites),
      m_curTargetLinks(src.m_curTargetLinks),
      m_curTargetLinkweight(src.m_curTargetLinkweight),
      m_oldTargetSites(src.m_oldTargetSites),
      m_oldLiveSites(src.m_oldLiveSites),
      m_oldTargetLinks(src.m_oldTargetLinks),
      m_newTargetLinks(src.m_newTargetLinks),
      m_newTargetLinkweight(src.m_newTargetLinkweight)
{
    // Intentionally blank; everything is accomplished in initialization list above.
} // Copy constructor for RecRange class.

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// The value of "other" (a reference to a Range object) is being cast to RecRange and then used
// to dereference "m_liveSites", not the value of the "m_liveSites" variable of the THIS object.

bool RecRange::operator==(const Range & other) const
{
    return (m_liveSites == dynamic_cast<const RecRange &>(other).m_liveSites &&
            m_transmittedSites == dynamic_cast<const RecRange &>(other).m_transmittedSites &&
            m_curTargetLinks == dynamic_cast<const RecRange &>(other).m_curTargetLinks);

} // RecRange::operator==

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).  Called only on recombinant trees, branches, RecRanges.
//
// Assume that the transmitted sites set is one contiguous set (ie, a single recombination point
// divides the full set into two sets, each represented by a lower-closed and upper-open interval.
// If recombination happens, one of those sets is transmitted and the other is not.  In this case,
// this function returns the Littlelink index of the link separating those two sets.  There are
// three cases:
//
//   A: No recombination; ALL sites are transmitted - lowest has index 0, high open endpoint
//      (one above highest transmitted) has index = total number of sites.  Return FLAGLONG
//      (not a valid value for ANY site or link index) to indicate NO RECOMBINATION.
//
//   B: Recombination divides set; LOW sites ARE transmitted; HIGH sites are NOT.  Recpoint
//      Littlelink is the one just above highest transmitted site, which has index of one less
//      than index of open upper endpoint of transmitted sites interval.  Return Littlelink index.
//
//   C: Recombination divides set; HIGH sites ARE transmitted; LOW sites are NOT.  Recpoint
//      Littlelink is the one just below lowest transmitted site, which has same index as
//      highest site NOT transmitted, which is same as index of lowest site TRANSMITTED
//      MINUS ONE.  Return Littlelink index.

long int RecRange::GetRecpoint() const
{
    if (m_transmittedSites.begin()->first == 0) // LOW sites YES, HIGH sites YES or NO.
    {
        if (m_transmittedSites.begin()->second == s_numRegionSites) // HIGH sites YES.
        {
            // ALL sites ARE transmitted ==> No recombination; return flag indicating so.
            return FLAGLONG;            // Case A.
        }
        else                            // HIGH sites NO.
        {
            // LOW sites ARE transmitted but HIGH sites are NOT; return the Littlelink
            // to right (just above) the HIGHEST site transmitted (numbered same as that site).
            // SECOND indexes the first site NOT transmitted (open upper end); hence the "- 1".
            return m_transmittedSites.begin()->second - 1; // Case B.
        }
    }
    else                                // LOW sites NO, HIGH sites YES.
    {
        // If in debug mode, check that HIGH sites are indeed transmitted.
        assert(m_transmittedSites.begin()->second == s_numRegionSites);

        // LOW sites are NOT transmitted but HIGH sites ARE; return the Littlelink to left
        // (just below) the LOWEST site transmitted (Link is numbered same as the Site just
        // below it, which is the HIGHEST site NOT transmitted or one less than the LOWEST
        // site that IS transmitted).
        return m_transmittedSites.begin()->first - 1; // Case C.
    }

} // RecRange::GetRecpoint

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::UpdateCRange(const Range * const child1rangeptr, const Range * const child2rangeptr,
                            const rangeset & fcsites, bool dofc)
{
    m_liveSites = Union(child1rangeptr->GetLiveSites(), child2rangeptr->GetLiveSites());

    rangeset targetsites(m_liveSites);
    if (dofc) targetsites = RemoveRangeFromRange(fcsites, m_liveSites);
    targetsites = Union(m_diseaseSites, targetsites);
    m_curTargetLinks = LinksSpanningSites(targetsites);
    m_curTargetLinkweight = AccumulatedLinkweight(m_curTargetLinks);
    m_newTargetLinks = RemoveRangeFromRange(m_oldTargetLinks, m_curTargetLinks);
    m_newTargetLinkweight = AccumulatedLinkweight(m_newTargetLinks);

#ifndef NDEBUG
    TestInvariants();
#endif

} // RecRange::UpdateCRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::UpdateRootRange(const Range * const child1rangeptr, const Range * const child2rangeptr,
                               const rangeset & fcsites, bool dofc)
{
    m_liveSites = Union(child1rangeptr->GetLiveSites(), child2rangeptr->GetLiveSites());

    rangeset targetsites(m_liveSites);
    if (dofc) targetsites = RemoveRangeFromRange(fcsites, m_liveSites);
    targetsites = Union(m_diseaseSites, targetsites);
    m_curTargetLinks = LinksSpanningSites(targetsites);
    m_curTargetLinkweight = AccumulatedLinkweight(m_curTargetLinks);

    // All sites were live and targetable on the old root, so there are no new target sites.
    m_newTargetLinks.clear();
    m_newTargetLinkweight = ZERO;
    m_oldTargetSites = GetAllSites();
    m_oldTargetLinks = GetAllLinks();   // See "NOTA BENE" in definition of class RecRange, file "range.h".

#ifndef NDEBUG
    TestInvariants();
#endif

} // RecRange::UpdateRootRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::UpdateOneLeggedCRange(const Range * const childrangeptr)
{
    m_liveSites = childrangeptr->GetLiveSites();

    m_curTargetLinks = childrangeptr->GetCurTargetLinks();
    m_curTargetLinkweight = childrangeptr->GetCurTargetLinkweight();

    // As a one-legged coalescence may coincide with a change in m_oldTargetLinks,
    // we must recalculate m_newTargetLinks here.
    m_newTargetLinks = RemoveRangeFromRange(m_oldTargetLinks, m_curTargetLinks);
    m_newTargetLinkweight = AccumulatedLinkweight(m_newTargetLinks);

#ifndef NDEBUG
    TestInvariants();
#endif

} // RecRange::UpdateOneLeggedCRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::UpdateOneLeggedRootRange(const Range * const childrangeptr)
{
    m_liveSites = childrangeptr->GetLiveSites();

    m_curTargetLinks = childrangeptr->GetCurTargetLinks();
    m_curTargetLinkweight = childrangeptr->GetCurTargetLinkweight();

    // All sites were live and targetable on the old root, so there are no new target sites.
    m_newTargetLinks.clear();
    m_newTargetLinkweight = ZERO;
    m_oldTargetSites = GetAllSites();
    m_oldTargetLinks = GetAllLinks();   // See "NOTA BENE" in definition of class RecRange, file "range.h".

#ifndef NDEBUG
    TestInvariants();
#endif

} // RecRange::UpdateOneLeggedRootRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::UpdateMRange(const Range * const child1rangeptr)
{
    m_liveSites = child1rangeptr->GetLiveSites();

    m_curTargetLinks = child1rangeptr->GetCurTargetLinks();
    m_curTargetLinkweight = child1rangeptr->GetCurTargetLinkweight();

    m_newTargetLinks = child1rangeptr->GetNewTargetLinks();
    m_newTargetLinkweight = child1rangeptr->GetNewTargetLinkweight();

#ifndef NDEBUG
    TestInvariants();
#endif // NDEBUG

} // RecRange::UpdateMRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Called only in RBranch::UpdateBranchRange (on a recombinant branch); ASSERTs in a non-recombinant situation.

void RecRange::UpdateRRange(const Range * const child1rangeptr, const rangeset & fcsites, bool dofc)
{
    m_liveSites = Intersection(child1rangeptr->GetLiveSites(), m_transmittedSites);

    rangeset targetsites(m_liveSites);
    if (dofc) targetsites = RemoveRangeFromRange(fcsites, targetsites);
    targetsites = Union(m_diseaseSites, targetsites);

    m_curTargetLinks = LinksSpanningSites(targetsites);
    m_curTargetLinkweight = AccumulatedLinkweight(m_curTargetLinks);

    m_newTargetLinks = RemoveRangeFromRange(m_oldTargetLinks, m_curTargetLinks);
    m_newTargetLinkweight = AccumulatedLinkweight(m_newTargetLinks);

#ifndef NDEBUG
    TestInvariants();
#endif // NDEBUG

} // RecRange::UpdateRRange

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Called only in RecTree::RecombineInactive (recombinant case).

bool RecRange::AreLowSitesOnInactiveBranch(long int recpoint) const
{
    // If all disease site(s) are on one side, that's the inactive side.
    // For now, we consider only a single site as possibly being "diseased".
    if ( ! m_diseaseSites.empty() )
    {
        // -1 because it's a half-open interval; we want the actual last site.
        // Less-than or EQUAL because RECPOINT is indexed same as the SITE to its LEFT.
        return (m_diseaseSites.rbegin()->second - 1) <= recpoint;
    }

    // If all old target sites are on one side, that's the inactive side.
    if ( ! m_oldTargetSites.empty() )
    {
        // There are some old target sites, and if ALL of them are on one side, that's the inactive side.
        bool recpointBelowLowestOldTargetSite = (recpoint < (m_oldTargetSites.begin()->first));
        bool recpointAboveHighestOldTargetSite = ((m_oldTargetSites.rbegin()->second - 1) <= recpoint);

        // Assert that all the old target sites are on the same side of the recpoint.
        assert( recpointBelowLowestOldTargetSite || recpointAboveHighestOldTargetSite );

        // If all the old target sites are on the low side, the low side is the inactive side.
        return recpointAboveHighestOldTargetSite;
    }

    // Beyond this point, for the branch to be valid (and thus not pruned out by now):
    //    (1) Final Coalescence must be activated,
    //    (2) Some old live sites must exist on this branch,
    //    (3) SOME of those old live sites must have been FC on the previous arrangement, and
    //    (4) ALL of those old live sites must have been FC.
    // In other words, ALL non-DEAD sites on this branch must have been FC on previous arrangement.
    // Since we don't store FC sites from previous arrangements, we can't test those conditions.
    //
#ifndef NDEBUG                          // We will ASSERT here, so may as well print a message as to why.
    if ( ! FINAL_COALESCENCE_ON || m_oldLiveSites.empty() )
    {
        DebugPrint2(FINAL_COALESCENCE_ON, recpoint);
        PrintInfo();
        assert( FINAL_COALESCENCE_ON );      // Final Coalescence must be activated.
        assert( ! m_oldLiveSites.empty() );  // SOME old live sites must exist on this branch.
    }
#endif

    // There are some old live sites (which have gone FC), and if ALL are on one side, that's the inactive side.
    bool recpointBelowLowestOldLiveSite = (recpoint < (m_oldLiveSites.begin()->first));
    bool recpointAboveHighestOldLiveSite = ((m_oldLiveSites.rbegin()->second - 1) <= recpoint);
    if (recpointBelowLowestOldLiveSite || recpointAboveHighestOldLiveSite)
    {
        return recpointAboveHighestOldLiveSite;
    }

    // But if they are NOT all on the same side, then we make a 50/50 random choice of inactive side.
    return registry.GetRandom().Bool();

} // RecRange::AreLowSitesOnInactiveBranch

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

bool RecRange::AreDiseaseSitesTransmitted() const
{
    if (m_diseaseSites.empty()) return true;

    for (set<rangepair>::const_iterator ri = m_diseaseSites.begin(); ri != m_diseaseSites.end(); ++ri)
    {
        // RSGNOTE:  If there is only a single disease site, this works.  But we may want it to work
        // in general for arbitrary sets of (multiple) disease sites.  Will have to fix it then.
        if (!IsInRangeset(m_transmittedSites, ri->first)) return false;
    }

    return true;

} // RecRange::AreDiseaseSitesTransmitted

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::ClearNewTargetLinks()
{
    m_newTargetLinks = linkrangeset();
    m_newTargetLinkweight = ZERO;

} // RecRange::ClearNewTargetLinks

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::SetOldInfoToCurrent()
{
    m_oldLiveSites = m_liveSites;
    m_oldTargetLinks = m_curTargetLinks;

} // RecRange::SetOldInfoToCurrent()

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

void RecRange::ResetOldTargetSites(const rangeset & fcsites)
{
    m_oldTargetSites = Union(m_diseaseSites, RemoveRangeFromRange(fcsites, m_liveSites));
} // RecRange::ResetOldTargetSites

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).

bool RecRange::AreChildTargetSitesTransmitted(const Range * const childrangeptr, const rangeset & fcsites) const
{
    rangeset childtargetsites(Union(childrangeptr->GetDiseaseSites(),
                                    RemoveRangeFromRange(fcsites, childrangeptr->GetLiveSites())));
    rangeset included =  Intersection(childtargetsites, m_transmittedSites);
    return (!included.empty());

} // RecRange::AreChildTargetSitesTransmitted

//------------------------------------------------------------------------------------
// Virtual function (Range and RecRange).
// Called only by debugging function; virtual (must work differently on recombinant and non-recombinant Ranges).

bool RecRange::SameAsChild(const Range * const childrangeptr) const
{
    return m_liveSites == childrangeptr->GetLiveSites() && m_curTargetLinks == childrangeptr->GetCurTargetLinks();
} // RecRange::SameAsChild

//------------------------------------------------------------------------------------
// Utility function in RecRange class only.
// Accumulates the total weight represented by a LINKRANGESET of Links.

Linkweight RecRange::AccumulatedLinkweight(const linkrangeset & setoflinks)
{
    if (setoflinks.empty()) return ZERO;

    Linkweight recweight(ZERO);
    for (linkrangesetconstiter link_iter = setoflinks.begin() ; link_iter != setoflinks.end() ; ++link_iter)
    {
#ifdef RUN_BIGLINKS
        // Biglink version is static member function; needs access to Biglink Vector Map.
        const unsigned long int lower_index(link_iter->first);
        const unsigned long int upper_index(link_iter->second);
        //
#if 1
        // Since "lower_index" is UNSIGNED, "lower_index < s_biglink_vectormap.size()" is also equivalent to "lower_index >= 0".
        DebugAssert2(lower_index < s_biglink_vectormap.size(), lower_index, s_biglink_vectormap.size());
#else   // Equivalent to DebugAssert2 above, in case it is removed later.
        assert(lower_index < s_biglink_vectormap.size());
#endif
        //
#if 1
        // "upper_index" is the "one-beyond" value; it is the limit at which a vector index value becomes invalid.
        // Since "upper_index" is UNSIGNED, "upper_index <= s_biglink_vectormap.size()" is also equivalent to "upper_index >= 0".
        DebugAssert2(upper_index <= s_biglink_vectormap.size(), upper_index, s_biglink_vectormap.size());
#else   // Equivalent to DebugAssert2 above, in case it is removed later.
        assert(upper_index <= s_biglink_vectormap.size());
#endif
        //
        for (unsigned long int index = lower_index ; index < upper_index ; ++index)
        {
            recweight += s_biglink_vectormap[index].GetBiglinkWeight();
        }
        //
#else   // RUN_BIGLINKS
        //
        // Littlelink version simply counts number of Littlelinks in its input LINKRANGESET.
        recweight += link_iter->second - link_iter->first;
        //
#endif  // RUN_BIGLINKS
    }

#if 0
    DebugPrint1(recweight);
#endif // 0

    return recweight;

} // RecRange::AccumulatedLinkweight

//------------------------------------------------------------------------------------
// Utility function in RecRange class only.
// Static member function; needs access to Biglink Vector Map but is otherwise independent of RecRange objects.
//
// Returns a LINKRANGESET consisting of a singleton LINKRANGEPAIR denoting a set of Biglinks spanning the input sites.
//
// Disease sites (if any) are treated as variant sites in construction of the Biglink Map.
// Therefore, output is the interval spanning the lowest Biglink bounded by a variant or a disease
// site to the highest Biglink bounded by such in the LINKRANGESET presented as input.
//
// "Spanning" means returning the LINKRANGESET consisting of a single LINKRANGEPAIR whose first element denotes the Biglink
// bounded (on its LOWER edge) by the LOWEST included target site and whose second element denotes the the Biglink
// bounded (on its UPPER edge) by the HIGHEST included target site (or by the FIRST EXCLUDED target site on its
// upper boundary, using "half-open interval terminology").  That "upper Biglink", again using "half-open interval
// terminology", has as its index (in the Biglink Vector Map) the index of the FIRST EXCLUDED Biglink, the one
// just above the "last INCLUDED" Biglink.  Such an "upper Biglink" would be non-existent, that is, identified by
// the index of the "just beyond the end" vector entry in the Map, if the LINKRANGEPAIR second element is denoting the
// highest-indexed entry in the Map.

#ifdef RUN_BIGLINKS

linkrangeset RecRange::LinksSpanningSites(const rangeset & targetsites)
{
    linkrangeset curtargetlinks;        // Create an empty LINKRANGESET.

    if (targetsites.empty())
    {
        return curtargetlinks;          // If no targetsites, return the empty LINKRANGESET.
    }
    else
    {
        const long int lower_targetsite(targetsites.begin()->first);       // Lowest INCLUDED target site.
        const long int upper_targetsite(targetsites.rbegin()->second - 1); // Highest target site (LAST-INCLUDED).
        const unsigned long int mapend_index(s_biglink_vectormap.size());  // Excluded upper map end index.

        // If the First-to-Last INCLUDED site indices span a non-empty interval and are in the correct order, proceed.
        if (lower_targetsite < upper_targetsite)
        {
            unsigned long int track_biglinkmap_idx;               // Tracks from below here.
            unsigned long int lower_biglinkmap_idx(mapend_index); // Included lower index starts at high (EXCLUDED) end.
            unsigned long int upper_biglinkmap_idx(0UL);          // Excluded upper index starts at low (INCLUDED) end.

            // Seek up from bottom; find lowest Biglink whose low end is at or above low end of target site interval.
            BiglinkVectormap::const_iterator vecmapiter;
            //
            // "track_biglinkmap_idx" here finds the first (from the bottom) BiglinkMapitem entry
            // whose lower-end Littlelink index is EQUAL TO or ABOVE "lower_targetsite".
            //
            for (vecmapiter = s_biglink_vectormap.begin(), track_biglinkmap_idx = 0UL;
                 vecmapiter != s_biglink_vectormap.end();
                 ++vecmapiter, ++track_biglinkmap_idx)
            {
                if (vecmapiter->GetBiglinkLowerLittlelink() >= lower_targetsite)
                {
                    lower_biglinkmap_idx = track_biglinkmap_idx;
                    break;
                }
            }

            // Seek down from top; find highest Biglink whose high end is at or below high end of target site interval.
            BiglinkVectormap::const_reverse_iterator revmapiter;
            //
            // "track_biglinkmap_idx" here tracks the first (from the top) BiglinkMapitem entry whose upper-end
            // Littlelink index is BELOW or EQUAL TO "upper_targetsite".  It is initialized to the "one-beyond" or
            // "FIRST-EXCLUDED" value (size of Biglink Vector Map) and decrements from there; "revmapiter" starts
            // with the highest INCLUDED Biglink and decrements.  Thus when the IF triggers, the value assigned to
            // "upper_biglinkmap_idx will be the index "one-beyond" that Biglink, ie, a "FIRST-EXCLUDED" value.
            //
            for (revmapiter = s_biglink_vectormap.rbegin(), track_biglinkmap_idx = mapend_index;
                 revmapiter != s_biglink_vectormap.rend();
                 ++revmapiter, --track_biglinkmap_idx)
            {
                if (revmapiter->GetBiglinkUpperLittlelink() <= upper_targetsite)
                {
                    upper_biglinkmap_idx = track_biglinkmap_idx;     // Already is FIRST-EXCLUDED value; don't add one.
                    break;
                }
            }

            // "lower_biglinkmap_idx" points to lower (INCLUDED) BiglinkMapitem entry and "upper_biglinkmap_idx" points
            // to upper (EXCLUDED) BiglinkMapitem entry of a contiguous set of Biglinks spanning the target sites.
            if (lower_biglinkmap_idx < upper_biglinkmap_idx)
            {
                // Normally, these endpoints will be in the proper order (this branch of IF), and we
                // return a LINKRANGESET consisting of the single LINKRANGEPAIR spanning that interval.
                linkrangepair newlinks(lower_biglinkmap_idx, upper_biglinkmap_idx);
                return AddPairToRange(newlinks, curtargetlinks);
            }
            else
            {
                // But it is possible (if enough sites are removed due to going FC that ALL target sites are inside
                // a single Biglink) that the "lowest above low end" Biglink is ABOVE the "highest below high end"
                // Biglink.  In that case, NO Biglinks "span" the target site set, and we return an empty rangeset.
                return curtargetlinks;
            }
        }
        else
        {
            // Sites ("targetsites") exist but don't span anything (maybe the set consists of only a single site);
            // return an empty LINKRANGESET again.
            return curtargetlinks;
        }
    }
} // RecRange::LinksSpanningSites

#else // RUN_BIGLINKS

// Also static, since called in RecRange namespace and without reference to an object,
// although Littlelink version does not need access to any static member variables.

linkrangeset RecRange::LinksSpanningSites(const rangeset & targetsites)
{
    linkrangeset curtargetlinks;

    // If the targetsites are empty we really do want to return an empty LINKRANGESET.
    if (!targetsites.empty())
    {
        const unsigned long int firstsite(targetsites.begin()->first);
        const unsigned long int lastsite(targetsites.rbegin()->second - 1);

        if (firstsite < lastsite)
        {
            linkrangepair newlinks(firstsite, lastsite);
            curtargetlinks = AddPairToRange(newlinks, curtargetlinks);
        }
    }

    return curtargetlinks;
} // RecRange::LinksSpanningSites

#endif // RUN_BIGLINKS

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).
// Prints in "internal" units, not "user" units.

void RecRange::PrintLive() const
{
    cerr << "Live Sites:             " << ToString(m_liveSites) << endl;
} // RecRange::PrintLive

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).
// Prints in "internal" units, not "user" units.

void RecRange::PrintInfo() const
{
    cerr << "RecRange::PrintInfo() ..." << endl << endl;
    cerr << "Total number of Sites:  " << s_numRegionSites << endl;
    cerr << "Disease Sites:          " << ToString(m_diseaseSites) << endl;
    cerr << "Transmitted Sites:      " << ToString(m_transmittedSites) << endl;

    PrintLive();

    cerr << "Old Live Sites:         " << ToString(m_oldLiveSites) << endl;
    cerr << "Current Target Links:   ";
    PrintLinks(m_curTargetLinks);

    PrintNewTargetLinks();

    cerr << "Old Target Sites:       " << ToString(m_oldTargetSites) << endl;
    cerr << "Old Target Links:       ";
    PrintLinks(m_oldTargetLinks);

    cerr << "Cur Target Linkweight:  " << m_curTargetLinkweight << endl;
    cerr << "New Target Linkweight:  " << m_newTargetLinkweight << endl << endl;

} // RecRange::PrintInfo

//------------------------------------------------------------------------------------
// Debugging function.
// Virtual function (Range and RecRange).
// Prints in "internal" units, not "user" units.

void RecRange::PrintNewTargetLinks() const
{
    cerr << "Newly Targetable Links:  ";
    PrintLinks(m_newTargetLinks);

} // RecRange::PrintNewTargetLinks

//------------------------------------------------------------------------------------
// Debugging function.
// Utility function (RecRange-only).
// Static member function; needs access to Biglink Vector Map but is otherwise independent of RecRange objects.

void RecRange::PrintLinks(const linkrangeset & setoflinks)
{
    if (setoflinks.empty())
    {
        cerr << "(none)" << endl;
    }
    else
    {
        linkrangesetconstiter biglink_iter = setoflinks.begin();

#ifdef RUN_BIGLINKS
        //
        for ( ; biglink_iter != setoflinks.end() ; ++biglink_iter)
        {
            const unsigned long int lower_index(biglink_iter->first);
            const unsigned long int upper_index(biglink_iter->second);
            double weightsum(0.0);
            long int counter(0);
            //
            assert(lower_index < s_biglink_vectormap.size());
            assert(upper_index <= s_biglink_vectormap.size());
            //
            cerr << "Biglink, map index range [" << lower_index << "," << upper_index << "), includes these Littlelinks:" << endl;
            for (unsigned long int index = lower_index ; index < upper_index ; ++index)
            {
                double linkweight(s_biglink_vectormap[index].GetBiglinkWeight());
                cerr << "    [" << s_biglink_vectormap[index].GetBiglinkLowerLittlelink()
                     << "," << s_biglink_vectormap[index].GetBiglinkUpperLittlelink()
                     << ")Wt:" << linkweight;
                weightsum += linkweight;
                if (++counter == 10)
                {
                    cerr << endl;
                    counter = 0;
                }
            }
            if (counter > 0)
            {
                cerr << endl;
            }
            cerr << "    Total Accumulated Weight:  " << weightsum << endl;
        }
        //
#else
        ToString(setoflinks);
        //
#endif // RUN_BIGLINKS
    }

} // RecRange::PrintLinks

//------------------------------------------------------------------------------------
// Debugging function; RecRange only.
// Prints in "internal" units, not "user" units.

void RecRange::PrintDiseaseSites() const
{
    cerr << "Disease Sites:           " << ToString(m_diseaseSites) << endl;
} // RecRange::PrintDiseaseSites

//------------------------------------------------------------------------------------
// Debugging function; RecRange-only.

void RecRange::PrintRightOrLeft() const
{
    cerr << "Right or left:  ";
    if (m_transmittedSites.begin()->first != 0L)
    {
        cerr << "left";
    }
    else if (m_transmittedSites.begin()->second != s_numRegionSites)
    {
        cerr << "right";
    }
    else
    {
        cerr << "unset";
    }

    cerr << endl;

} // RecRange::PrintRightOrLeft

//------------------------------------------------------------------------------------
// Debugging function in RecRange class only.

void RecRange::TestInvariants() const
{
    // JDEBUG -- augment to test that the "num" variables are correct; possibly also add a rangeset validator.
    bool good = true;

    for (set<rangepair>::const_iterator ri = m_diseaseSites.begin(); ri != m_diseaseSites.end(); ri++)
    {
        if (ri->first < 0L) good = false;
        if (ri->first >= s_numRegionSites) good = false;
    }

    assert(good);

} // RecRange::TestInvariants

//------------------------------------------------------------------------------------
// Helper debugging function at global scope.
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// Dumps data used for building Biglink Vector Map (for debugging).

#ifdef ENABLE_REGION_DUMP

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

void PrintRegionData(long int regionnum, const Region & curregion)
{
    // This function writes to STANDARD-ERROR, which (hopefully) redirects to a file in a unique directory for each test run.
    const long int numloci(curregion.GetNloci());
    const long int numtips(curregion.GetNTips());   // All loci in region have data for same number of tips.

    cerr << "Region Name (name of Region):            \"" << curregion.GetRegionName() << "\"" << endl;
    cerr << "Region Number (index of Region):         " << regionnum << endl;
    cerr << "Number of Sites in this Region:          " << curregion.GetNumSites() << endl; // All sites, whether in a locus or between them.
    cerr << "Number of Loci in this Region:           " << numloci << endl;
    cerr << "Number of Tips for each Locus:           " << numtips << endl << endl;

    for (long int locIdx = 0L; locIdx < numloci; ++locIdx)
    {
        const Locus & locus(curregion.GetLocus(locIdx));
        const long int regionalmapposition(locus.GetRegionalMapPosition());
        const long int nmarkers(locus.GetNmarkers());
        const data_type datatype_enum(locus.GetDataType());
        const DataType_ptr datatype_ptr(locus.GetDataTypePtr());

        cerr << "Name of this Locus in Region:            " << locus.GetName() << endl;
        cerr << "Index of this Locus in Region:           " << locus.GetIndex() << " (locus " << locIdx + 1 << " of " << numloci << ")" << endl;
        cerr << "DataType stored in this Locus:           " << datatype_enum << " (type " << ToString(datatype_enum) << ")" << endl;
        cerr << "Number of Markers in this Locus:         " << nmarkers << endl;
        cerr << "Number of Sites in this Locus:           " << locus.GetNsites() << endl;
        cerr << "Offset of this Locus's sites in Region:  " << regionalmapposition << endl << endl;

        const LongVec1d markerVector(locus.GetMarkerLocations());
        assert(nmarkers == static_cast<long int>(markerVector.size())); // One is signed, other is unsigned.
        cerr << "GetMarkerLocations size (marker vector size for this locus):  " << nmarkers << " (shown if location of Site != index + RegionalMapPosition) ..." << endl;
        for (long int markerindex = 0L; markerindex < nmarkers; ++markerindex)
        {
            const long int val(markerVector[markerindex]);
            if ( val != (markerindex + regionalmapposition) )
            {
                // The "setw" manipulator works "per output field" and so must be re-applied each time needed.
                cerr << "GetMarkerLocations[" << setw(4) << markerindex << "]:  " << val << endl;
            }
        }
        cerr << endl;

        const vector<TipData> tipdatavec(locus.GetTipData());
        assert(nmarkers == static_cast<long int>(tipdatavec[0].data.size())); // One is signed, other is unsigned.

        StringVec1d compdata;
        compdata.reserve(nmarkers);     // Pre-allocate for speed.
        // Preload the comparison vector with one of the tip vectors (the zeroth, known to exist).
        for (long int markerindex = 0L; markerindex < nmarkers; ++markerindex)
        {
            compdata.push_back(tipdatavec[0].data[markerindex]);
        }

        // If disease site(s) are present, then include them; ie, treat them as variant sites in map construction.
        if (registry.GetForceSummary().CheckForce(force_DISEASE))
        {
            const ForceSummary & fs(registry.GetForceSummary());
            if (fs.GetNLocalPartitionForces() > 0L)
            {
                cerr << "Disease Site(s) present ..." << endl;
                const ForceVec lpforces(fs.GetLocalPartitionForces());
                for (ForceVec::const_iterator lpforce = lpforces.begin(); lpforce != lpforces.end(); ++lpforce)
                {
                    const LocalPartitionForce * lpf(dynamic_cast<const LocalPartitionForce *>(* lpforce));
                    assert(lpf);        // Make sure pointer is non-NULL.
                    cerr << "Disease Site at index:  " << lpf->GetLocalSite() << endl;
                }
            }
            cerr << endl;
        }

        for (long int tipindex = 0L; tipindex < numtips; ++tipindex)
        {
            // The "setw" manipulator works "per output field" and so must be re-applied each time needed.
            cerr << "Tip " << setw(4) << tipindex << " data:                    ";
            const TipData tipdata(tipdatavec[tipindex]);
            const StringVec1d markerdata(tipdata.data);
            for (long int markerindex = 0L; markerindex < nmarkers; ++markerindex)
            {
                cerr << markerdata[markerindex]; // Draw all marker data (iteratively printing each single-character string) for one Tip.
                // Compare each tip vector (>= 1) with the zeroth (stored in comparison vector).
                // If any pair of markers compares "non-IsEquivalent()", mark comparison vector slot with "*".
                // On zeroth iteration, tip 0 will compare with itself (this iteration is needed only to print that tip),
                // but since that comparison will always succeed, we waste time but don't change comparison vector then.
                if ( ! datatype_ptr->IsEquivalent(markerdata[markerindex], compdata[markerindex]) )
                {
                    compdata[markerindex] = "*";
                }
            }
            cerr << endl;
        }

        cerr << "Variant data:                     ";
        for (long int markerindex = 0L; markerindex < nmarkers; ++markerindex)
        {
            cerr << compdata[markerindex];
        }
        cerr << endl << endl;
    }
}

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

#endif // ENABLE_REGION_DUMP

//____________________________________________________________________________________
