/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include <stdlib.h>
#include <unotools/collatorwrapper.hxx>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>

#include <rangelst.hxx>
#include <document.hxx>
#include <refupdat.hxx>
#include <compiler.hxx>
#include <algorithm>
#include <memory>

using ::formula::FormulaGrammar;

namespace {

template<typename T>
class FindEnclosingRange
{
public:
    explicit FindEnclosingRange(const T& rTest) : mrTest(rTest) {}
    bool operator() (const ScRange & rRange) const
    {
        return rRange.Contains(mrTest);
    }
private:
    const T& mrTest;
};

template<typename T>
class FindIntersectingRange
{
public:
    explicit FindIntersectingRange(const T& rTest) : mrTest(rTest) {}
    bool operator() (const ScRange & rRange) const
    {
        return rRange.Intersects(mrTest);
    }
private:
    const T& mrTest;
};

class CountCells
{
public:
    CountCells() : mnCellCount(0) {}

    void operator() (const ScRange & r)
    {
        mnCellCount +=
              sal_uInt64(r.aEnd.Col() - r.aStart.Col() + 1)
            * sal_uInt64(r.aEnd.Row() - r.aStart.Row() + 1)
            * sal_uInt64(r.aEnd.Tab() - r.aStart.Tab() + 1);
    }

    sal_uInt64 getCellCount() const { return mnCellCount; }

private:
    sal_uInt64 mnCellCount;
};


}

//  ScRangeList
ScRangeList::~ScRangeList()
{
}

ScRefFlags ScRangeList::Parse( std::u16string_view rStr, const ScDocument& rDoc,
                           formula::FormulaGrammar::AddressConvention eConv,
                           SCTAB nDefaultTab, sal_Unicode cDelimiter )
{
    if ( !rStr.empty() )
    {
        if (!cDelimiter)
            cDelimiter = ScCompiler::GetNativeSymbolChar(ocSep);

        ScRefFlags nResult = ~ScRefFlags::ZERO;    // set all bits
        ScRange aRange;
        const SCTAB nTab = nDefaultTab;

        sal_Int32 nPos = 0;
        do
        {
            const OUString aOne( o3tl::getToken(rStr, 0, cDelimiter, nPos ) );
            aRange.aStart.SetTab( nTab );   // default tab if not specified
            ScRefFlags nRes = aRange.ParseAny( aOne, rDoc, eConv );
            ScRefFlags nEndRangeBits = ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
            ScRefFlags nTmp1 = nRes & ScRefFlags::BITS;
            ScRefFlags nTmp2 = nRes & nEndRangeBits;
            // If we have a valid single range with
            // any of the address bits we are interested in
            // set - set the equiv end range bits
            if ( (nRes & ScRefFlags::VALID ) && (nTmp1 != ScRefFlags::ZERO) && ( nTmp2 != nEndRangeBits ) )
                applyStartToEndFlags(nRes, nTmp1);

            if ( nRes & ScRefFlags::VALID )
                push_back( aRange );
            nResult &= nRes;        // all common bits are preserved
        }
        while (nPos >= 0);

        return nResult;             // ScRefFlags::VALID set when all are OK
    }
    else
        return ScRefFlags::ZERO;
}

void ScRangeList::Format( OUString& rStr, ScRefFlags nFlags, const ScDocument& rDoc,
                          formula::FormulaGrammar::AddressConvention eConv,
                          sal_Unicode cDelimiter, bool bFullAddressNotation ) const
{
    if (!cDelimiter)
        cDelimiter = ScCompiler::GetNativeSymbolChar(ocSep);

    OUStringBuffer aBuf;
    bool bFirst = true;
    for( auto const & r : maRanges)
    {
        if (bFirst)
            bFirst = false;
        else
            aBuf.append(OUStringChar(cDelimiter));
        aBuf.append(r.Format(rDoc, nFlags, eConv, bFullAddressNotation));
    }
    rStr = aBuf.makeStringAndClear();
}

void ScRangeList::Join( const ScRange& rNewRange, bool bIsInList )
{
    if ( maRanges.empty() )
    {
        push_back( rNewRange );
        return ;
    }

    // One common usage is to join ranges that actually are top to bottom
    // appends but the caller doesn't exactly know about it, e.g. when invoked
    // by ScMarkData::FillRangeListWithMarks(), check for this special case
    // first and speed up things by not looping over all ranges for each range
    // to be joined. We don't remember the exact encompassing range that would
    // have to be updated on refupdates and insertions and deletions, instead
    // remember just the maximum row used, even independently of the sheet.
    // This satisfies most use cases.

    if (!bIsInList)
    {
        const SCROW nRow1 = rNewRange.aStart.Row();
        if (nRow1 > mnMaxRowUsed + 1)
        {
            push_back( rNewRange );
            return;
        }
        else if (nRow1 == mnMaxRowUsed + 1)
        {
            // Check if we can simply enlarge the last range.
            ScRange & rLast = maRanges.back();
            if (rLast.aEnd.Row() + 1 == nRow1 &&
                    rLast.aStart.Col() == rNewRange.aStart.Col() && rLast.aEnd.Col() == rNewRange.aEnd.Col() &&
                    rLast.aStart.Tab() == rNewRange.aStart.Tab() && rLast.aEnd.Tab() == rNewRange.aEnd.Tab())
            {
                const SCROW nRow2 = rNewRange.aEnd.Row();
                rLast.aEnd.SetRow( nRow2 );
                mnMaxRowUsed = nRow2;
                return;
            }
        }
    }

    bool bJoinedInput = false;
    const ScRange* pOver = &rNewRange;

Label_Range_Join:

    assert(pOver);
    const SCCOL nCol1 = pOver->aStart.Col();
    const SCROW nRow1 = pOver->aStart.Row();
    const SCCOL nTab1 = pOver->aStart.Tab();
    const SCCOL nCol2 = pOver->aEnd.Col();
    const SCROW nRow2 = pOver->aEnd.Row();
    const SCCOL nTab2 = pOver->aEnd.Tab();

    size_t nOverPos = std::numeric_limits<size_t>::max();
    for (size_t i = 0; i < maRanges.size(); ++i)
    {
        ScRange & rRange = maRanges[i];
        if ( &rRange == pOver )
        {
            nOverPos = i;
            continue;           // the same one, continue with the next
        }
        bool bJoined = false;
        if ( rRange.Contains( *pOver ) )
        {   // range pOver included in or identical to range p
            // XXX if we never used Append() before Join() we could remove
            // pOver and end processing, but it is not guaranteed and there can
            // be duplicates.
            if ( bIsInList )
                bJoined = true;     // do away with range pOver
            else
            {   // that was all then
                bJoinedInput = true;    // don't append
                break;  // for
            }
        }
        else if ( pOver->Contains( rRange ) )
        {   // range rRange included in range pOver, make pOver the new range
            rRange = *pOver;
            bJoined = true;
        }
        if ( !bJoined && rRange.aStart.Tab() == nTab1 && rRange.aEnd.Tab() == nTab2 )
        {   // 2D
            if ( rRange.aStart.Col() == nCol1 && rRange.aEnd.Col() == nCol2 )
            {
                if ( rRange.aStart.Row() <= nRow2+1 &&
                     rRange.aStart.Row() >= nRow1 )
                {   // top
                    rRange.aStart.SetRow( nRow1 );
                    bJoined = true;
                }
                else if ( rRange.aEnd.Row() >= nRow1-1 &&
                          rRange.aEnd.Row() <= nRow2 )
                {   // bottom
                    rRange.aEnd.SetRow( nRow2 );
                    bJoined = true;
                }
            }
            else if ( rRange.aStart.Row() == nRow1 && rRange.aEnd.Row() == nRow2 )
            {
                if ( rRange.aStart.Col() <= nCol2+1 &&
                     rRange.aStart.Col() >= nCol1 )
                {   // left
                    rRange.aStart.SetCol( nCol1 );
                    bJoined = true;
                }
                else if ( rRange.aEnd.Col() >= nCol1-1 &&
                          rRange.aEnd.Col() <= nCol2 )
                {   // right
                    rRange.aEnd.SetCol( nCol2 );
                    bJoined = true;
                }
            }
        }
        if ( bJoined )
        {
            if ( bIsInList )
            {   // delete range pOver within the list
                if (nOverPos != std::numeric_limits<size_t>::max())
                {
                    Remove(nOverPos);
                    if (nOverPos < i)
                        --i;
                }
                else
                {
                    for (size_t nOver = 0, nRanges = maRanges.size(); nOver < nRanges; ++nOver)
                    {
                        if (&maRanges[nOver] == pOver)
                        {
                            Remove(nOver);
                            break;
                        }
                    }
                }
            }
            bJoinedInput = true;
            pOver = &maRanges[i];
            bIsInList = true;
            goto Label_Range_Join;
        }
    }
    if (  !bIsInList && !bJoinedInput )
        push_back( rNewRange );
}

void ScRangeList::AddAndPartialCombine( const ScRange& rNewRange )
{
    if ( maRanges.empty() )
    {
        push_back( rNewRange );
        return ;
    }

    // One common usage is to join ranges that actually are top to bottom
    // appends but the caller doesn't exactly know about it, e.g. when invoked
    // by ScMarkData::FillRangeListWithMarks(), check for this special case
    // first and speed up things by not looping over all ranges for each range
    // to be joined. We don't remember the exact encompassing range that would
    // have to be updated on refupdates and insertions and deletions, instead
    // remember just the maximum row used, even independently of the sheet.
    // This satisfies most use cases.

    const SCROW nRow1 = rNewRange.aStart.Row();
    if (nRow1 > mnMaxRowUsed + 1)
    {
        push_back( rNewRange );
        return;
    }

    // scan backwards 2 rows to see if we can merge with anything
    auto it = maRanges.rbegin();
    while (it != maRanges.rend() && it->aStart.Row() >= (rNewRange.aStart.Row() - 2))
    {
        // Check if we can simply enlarge this range.
        ScRange & rLast = *it;
        if (rLast.aEnd.Row() + 1 == nRow1 &&
                rLast.aStart.Col() == rNewRange.aStart.Col() && rLast.aEnd.Col() == rNewRange.aEnd.Col() &&
                rLast.aStart.Tab() == rNewRange.aStart.Tab() && rLast.aEnd.Tab() == rNewRange.aEnd.Tab())
        {
            const SCROW nRow2 = rNewRange.aEnd.Row();
            rLast.aEnd.SetRow( nRow2 );
            mnMaxRowUsed = std::max(mnMaxRowUsed, nRow2);
            return;
        }
        ++it;
    }

    push_back( rNewRange );
}

bool ScRangeList::operator==( const ScRangeList& r ) const
{
    if ( this == &r )
        return true;

    return maRanges == r.maRanges;
}

bool ScRangeList::operator!=( const ScRangeList& r ) const
{
    return !operator==( r );
}

bool ScRangeList::UpdateReference(
    UpdateRefMode eUpdateRefMode,
    const ScDocument& rDoc,
    const ScRange& rWhere,
    SCCOL nDx,
    SCROW nDy,
    SCTAB nDz
)
{
    if (maRanges.empty())
        // No ranges to update.  Bail out.
        return false;

    bool bChanged = false;
    SCCOL nCol1;
    SCROW nRow1;
    SCTAB nTab1;
    SCCOL nCol2;
    SCROW nRow2;
    SCTAB nTab2;
    rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );

    if(eUpdateRefMode == URM_INSDEL)
    {
        // right now this only works for nTab1 == nTab2
        if(nTab1 == nTab2)
        {
            if(nDx < 0)
            {
                bChanged = DeleteArea(nCol1+nDx, nRow1, nTab1, nCol1-1, nRow2, nTab2);
            }
            if(nDy < 0)
            {
                bChanged = DeleteArea(nCol1, nRow1+nDy, nTab1, nCol2, nRow1-1, nTab2);
            }
            SAL_WARN_IF(nDx < 0 && nDy < 0, "sc", "nDx and nDy are negative, check why");
        }
    }

    if(maRanges.empty())
        return true;

    for (auto& rR : maRanges)
    {
        SCCOL theCol1;
        SCROW theRow1;
        SCTAB theTab1;
        SCCOL theCol2;
        SCROW theRow2;
        SCTAB theTab2;
        rR.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
        if ( ScRefUpdate::Update( rDoc, eUpdateRefMode,
                nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
                nDx, nDy, nDz,
                theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
                != UR_NOTHING )
        {
            bChanged = true;
            rR.aStart.Set( theCol1, theRow1, theTab1 );
            rR.aEnd.Set( theCol2, theRow2, theTab2 );
            if (mnMaxRowUsed < theRow2)
                mnMaxRowUsed = theRow2;
        }
    }

    if(eUpdateRefMode == URM_INSDEL)
    {
        if( nDx < 0 || nDy < 0 )
        {
            size_t n = maRanges.size();
            for(size_t i = n-1; i > 0;)
            {
                Join(maRanges[i], true);
                // Join() may merge and remove even more than one item, protect against it.
                if(i >= maRanges.size())
                    i = maRanges.size()-1;
                else
                    --i;
            }
        }
    }

    return bChanged;
}

void ScRangeList::InsertRow( SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize )
{
    std::vector<ScRange> aNewRanges;
    for(const auto & rRange : maRanges)
    {
        if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
        {
            if(rRange.aEnd.Row() == nRowPos - 1 && (nColStart <= rRange.aEnd.Col() || nColEnd >= rRange.aStart.Col()))
            {
                SCCOL nNewRangeStartCol = std::max<SCCOL>(nColStart, rRange.aStart.Col());
                SCCOL nNewRangeEndCol = std::min<SCCOL>(nColEnd, rRange.aEnd.Col());
                SCROW nNewRangeStartRow = rRange.aEnd.Row() + 1;
                SCROW nNewRangeEndRow = nRowPos + nSize - 1;
                aNewRanges.emplace_back(nNewRangeStartCol, nNewRangeStartRow, nTab, nNewRangeEndCol,
                            nNewRangeEndRow, nTab);
                if (mnMaxRowUsed < nNewRangeEndRow)
                    mnMaxRowUsed = nNewRangeEndRow;
            }
        }
    }

    for(const auto & rRange : aNewRanges)
    {
        if(!rRange.IsValid())
            continue;

        Join(rRange);
    }
}

void ScRangeList::InsertCol( SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize )
{
    std::vector<ScRange> aNewRanges;
    for(const auto & rRange : maRanges)
    {
        if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
        {
            if(rRange.aEnd.Col() == nColPos - 1 && (nRowStart <= rRange.aEnd.Row() || nRowEnd >= rRange.aStart.Row()))
            {
                SCROW nNewRangeStartRow = std::max<SCROW>(nRowStart, rRange.aStart.Row());
                SCROW nNewRangeEndRow = std::min<SCROW>(nRowEnd, rRange.aEnd.Row());
                SCCOL nNewRangeStartCol = rRange.aEnd.Col() + 1;
                SCCOL nNewRangeEndCol = nColPos + nSize - 1;
                aNewRanges.emplace_back(nNewRangeStartCol, nNewRangeStartRow, nTab, nNewRangeEndCol,
                            nNewRangeEndRow, nTab);
            }
        }
    }

    for(const auto & rRange : aNewRanges)
    {
        if(!rRange.IsValid())
            continue;

        Join(rRange);
    }
}

void ScRangeList::InsertCol( SCTAB nTab, SCCOL nCol )
{
    std::vector<ScRange> aNewRanges;
    for(const auto & rRange : maRanges)
    {
        if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
        {
            if(rRange.aEnd.Col() == nCol - 1)
            {
                SCCOL nNewRangeStartCol = rRange.aEnd.Col() + 1;
                SCCOL nNewRangeEndCol = nCol;
                aNewRanges.emplace_back(nNewRangeStartCol, rRange.aStart.Row(), nTab, nNewRangeEndCol,
                            rRange.aEnd.Row(), nTab);
            }
        }
    }

    for(const auto & rRange : aNewRanges)
    {
        if(!rRange.IsValid())
            continue;

        Join(rRange);
    }
}

namespace {

/**
 * Check if the deleting range cuts the test range exactly into a single
 * piece.
 *
 * X = column ; Y = row
 * +------+    +------+
 * |xxxxxx|    |      |
 * +------+ or +------+
 * |      |    |xxxxxx|
 * +------+    +------+
 *
 * X = row; Y = column
 * +--+--+    +--+--+
 * |xx|  |    |  |xx|
 * |xx|  | or |  |xx|
 * |xx|  |    |  |xx|
 * +--+--+    +--+--+
 * where xxx is the deleted region.
 */
template<typename X, typename Y>
bool checkForOneRange(
   X nDeleteX1, X nDeleteX2, Y nDeleteY1, Y nDeleteY2, X nX1, X nX2, Y nY1, Y nY2)
{
    return nDeleteX1 <= nX1 && nX2 <= nDeleteX2 && (nDeleteY1 <= nY1 || nY2 <= nDeleteY2);
}

bool handleOneRange( const ScRange& rDeleteRange, ScRange& r )
{
    const ScAddress& rDelStart = rDeleteRange.aStart;
    const ScAddress& rDelEnd = rDeleteRange.aEnd;
    ScAddress aPStart = r.aStart;
    ScAddress aPEnd = r.aEnd;
    SCCOL nDeleteCol1 = rDelStart.Col();
    SCCOL nDeleteCol2 = rDelEnd.Col();
    SCROW nDeleteRow1 = rDelStart.Row();
    SCROW nDeleteRow2 = rDelEnd.Row();
    SCCOL nCol1 = aPStart.Col();
    SCCOL nCol2 = aPEnd.Col();
    SCROW nRow1 = aPStart.Row();
    SCROW nRow2 = aPEnd.Row();

    if (checkForOneRange(nDeleteCol1, nDeleteCol2, nDeleteRow1, nDeleteRow2, nCol1, nCol2, nRow1, nRow2))
    {
        // Deleting range fully overlaps the column range.  Adjust the row span.
        if (nDeleteRow1 <= nRow1)
        {
            // +------+
            // |xxxxxx|
            // +------+
            // |      |
            // +------+ (xxx) = deleted region

            r.aStart.SetRow(nDeleteRow1+1);
            return true;
        }
        else if (nRow2 <= nDeleteRow2)
        {
            // +------+
            // |      |
            // +------+
            // |xxxxxx|
            // +------+ (xxx) = deleted region

            r.aEnd.SetRow(nDeleteRow1-1);
            return true;
        }
    }
    else if (checkForOneRange(nDeleteRow1, nDeleteRow2, nDeleteCol1, nDeleteCol2, nRow1, nRow2, nCol1, nCol2))
    {
        // Deleting range fully overlaps the row range.  Adjust the column span.
        if (nDeleteCol1 <= nCol1)
        {
            // +--+--+
            // |xx|  |
            // |xx|  |
            // |xx|  |
            // +--+--+ (xxx) = deleted region

            r.aStart.SetCol(nDeleteCol2+1);
            return true;
        }
        else if (nCol2 <= nDeleteCol2)
        {
            // +--+--+
            // |  |xx|
            // |  |xx|
            // |  |xx|
            // +--+--+ (xxx) = deleted region

            r.aEnd.SetCol(nDeleteCol1-1);
            return true;
        }
    }
    return false;
}

bool handleTwoRanges( const ScRange& rDeleteRange, ScRange& r, std::vector<ScRange>& rNewRanges )
{
    const ScAddress& rDelStart = rDeleteRange.aStart;
    const ScAddress& rDelEnd = rDeleteRange.aEnd;
    ScAddress aPStart = r.aStart;
    ScAddress aPEnd = r.aEnd;
    SCCOL nDeleteCol1 = rDelStart.Col();
    SCCOL nDeleteCol2 = rDelEnd.Col();
    SCROW nDeleteRow1 = rDelStart.Row();
    SCROW nDeleteRow2 = rDelEnd.Row();
    SCCOL nCol1 = aPStart.Col();
    SCCOL nCol2 = aPEnd.Col();
    SCROW nRow1 = aPStart.Row();
    SCROW nRow2 = aPEnd.Row();
    SCTAB nTab = aPStart.Tab();

    if (nCol1 < nDeleteCol1 && nDeleteCol1 <= nCol2 && nCol2 <= nDeleteCol2)
    {
        // column deleted :     |-------|
        // column original: |-------|
        if (nRow1 < nDeleteRow1 && nDeleteRow1 <= nRow2 && nRow2 <= nDeleteRow2)
        {
            // row deleted:     |------|
            // row original: |------|
            //
            // +-------+
            // |   1   |
            // +---+---+---+
            // | 2 |xxxxxxx|
            // +---+xxxxxxx|
            //     |xxxxxxx|
            //     +-------+ (xxx) deleted region

            ScRange aNewRange( nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nRow2, nTab ); // 2
            rNewRanges.push_back(aNewRange);

            r.aEnd.SetRow(nDeleteRow1-1); // 1
            return true;
        }
        else if (nRow1 <= nDeleteRow2 && nDeleteRow2 < nRow2 && nDeleteRow1 <= nRow1)
        {
            // row deleted:  |------|
            // row original:    |------|
            //
            //     +-------+
            //     |xxxxxxx|
            // +---+xxxxxxx|
            // | 1 |xxxxxxx|
            // +---+---+---+
            // |   2   |    (xxx) deleted region
            // +-------+

            ScRange aNewRange( aPStart, ScAddress(nDeleteCol1-1, nRow2, nTab) ); // 1
            rNewRanges.push_back(aNewRange);

            r.aStart.SetRow(nDeleteRow2+1); // 2
            return true;
        }
    }
    else if (nCol1 <= nDeleteCol2 && nDeleteCol2 < nCol2 && nDeleteCol1 <= nCol1)
    {
        // column deleted : |-------|
        // column original:     |-------|
        if (nRow1 < nDeleteRow1 && nDeleteRow1 <= nRow2 && nRow2 <= nDeleteRow2)
        {
            // row deleted:     |------|
            // row original: |------|
            //
            //     +-------+
            //     |   1   |
            // +-------+---+
            // |xxxxxxx| 2 |
            // |xxxxxxx+---+
            // |xxxxxxx|
            // +-------+
            //  (xxx) deleted region

            ScRange aNewRange( ScAddress( nDeleteCol2+1, nDeleteRow1, nTab ), aPEnd ); // 2
            rNewRanges.push_back(aNewRange);

            r.aEnd.SetRow(nDeleteRow1-1); // 1
            return true;
        }
        else if (nRow1 <= nDeleteRow2 && nDeleteRow2 < nRow2 && nDeleteRow1 <= nRow1)
        {
            // row deleted:  |-------|
            // row original:     |--------|
            //
            // +-------+
            // |xxxxxxx|
            // |xxxxxxx+---+
            // |xxxxxxx| 1 |
            // +-------+---+
            //     |   2   |
            //     +-------+ (xxx) deleted region

            ScRange aNewRange(nDeleteCol2+1, nRow1, nTab, nCol2, nDeleteRow2, nTab); // 1
            rNewRanges.push_back(aNewRange);

            r.aStart.SetRow(nDeleteRow2+1); // 2
            return true;
        }
    }
    else if (nRow1 < nDeleteRow1 && nDeleteRow2 < nRow2 && nDeleteCol1 <= nCol1 && nCol2 <= nDeleteCol2)
    {
        // +--------+
        // |   1    |
        // +--------+
        // |xxxxxxxx| (xxx) deleted region
        // +--------+
        // |   2    |
        // +--------+

        ScRange aNewRange( aPStart, ScAddress(nCol2, nDeleteRow1-1, nTab) ); // 1
        rNewRanges.push_back(aNewRange);

        r.aStart.SetRow(nDeleteRow2+1); // 2
        return true;
    }
    else if (nCol1 < nDeleteCol1 && nDeleteCol2 < nCol2 && nDeleteRow1 <= nRow1 && nRow2 <= nDeleteRow2)
    {
        // +---+-+---+
        // |   |x|   |
        // |   |x|   |
        // | 1 |x| 2 | (xxx) deleted region
        // |   |x|   |
        // |   |x|   |
        // +---+-+---+

        ScRange aNewRange( aPStart, ScAddress(nDeleteCol1-1, nRow2, nTab) ); // 1
        rNewRanges.push_back(aNewRange);

        r.aStart.SetCol(nDeleteCol2+1); // 2
        return true;
    }

    return false;
}

/**
 * Check if any of the following applies:
 *
 * X = column; Y = row
 * +----------+           +----------+
 * |          |           |          |
 * |  +-------+---+    +--+-------+  |
 * |  |xxxxxxxxxxx| or |xxxxxxxxxx|  |
 * |  +-------+---+    +--+-------+  |
 * |          |           |          |
 * +----------+           +----------+
 *
 * X = row; Y = column
 *     +--+
 *     |xx|
 * +---+xx+---+    +----------+
 * |   |xx|   |    |          |
 * |   |xx|   | or |   +--+   |
 * |   +--+   |    |   |xx|   |
 * |          |    |   |xx|   |
 * +----------+    +---+xx+---+
 *                     |xx|
 *                     +--+     (xxx) deleted region
 */
template<typename X, typename Y>
bool checkForThreeRanges(
   X nDeleteX1, X nDeleteX2, Y nDeleteY1, Y nDeleteY2, X nX1, X nX2, Y nY1, Y nY2)
{
    if (nX1 <= nDeleteX1 && nX2 <= nDeleteX2 && nY1 < nDeleteY1 && nDeleteY2 < nY2)
        return true;

    if (nDeleteX1 <= nX1 && nDeleteX2 <= nX2 && nY1 < nDeleteY1 && nDeleteY2 < nY2)
        return true;

    return false;
}

bool handleThreeRanges( const ScRange& rDeleteRange, ScRange& r, std::vector<ScRange>& rNewRanges )
{
    const ScAddress& rDelStart = rDeleteRange.aStart;
    const ScAddress& rDelEnd = rDeleteRange.aEnd;
    ScAddress aPStart = r.aStart;
    ScAddress aPEnd = r.aEnd;
    SCCOL nDeleteCol1 = rDelStart.Col();
    SCCOL nDeleteCol2 = rDelEnd.Col();
    SCROW nDeleteRow1 = rDelStart.Row();
    SCROW nDeleteRow2 = rDelEnd.Row();
    SCCOL nCol1 = aPStart.Col();
    SCCOL nCol2 = aPEnd.Col();
    SCROW nRow1 = aPStart.Row();
    SCROW nRow2 = aPEnd.Row();
    SCTAB nTab = aPStart.Tab();

    if (checkForThreeRanges(nDeleteCol1, nDeleteCol2, nDeleteRow1, nDeleteRow2, nCol1, nCol2, nRow1, nRow2))
    {
        if (nCol1 < nDeleteCol1)
        {
            // +---+------+
            // |   |  2   |
            // |   +------+---+
            // | 1 |xxxxxxxxxx|
            // |   +------+---+
            // |   |  3   |
            // +---+------+

            ScRange aNewRange(nDeleteCol1, nRow1, nTab, nCol2, nDeleteRow1-1, nTab); // 2
            rNewRanges.push_back(aNewRange);

            aNewRange = ScRange(ScAddress(nDeleteCol1, nDeleteRow2+1, nTab), aPEnd); // 3
            rNewRanges.push_back(aNewRange);

            r.aEnd.SetCol(nDeleteCol1-1); // 1
        }
        else
        {
            //     +------+---+
            //     |  1   |   |
            // +---+------+   |
            // |xxxxxxxxxx| 2 |
            // +---+------+   |
            //     |  3   |   |
            //     +------+---+

            ScRange aNewRange(aPStart, ScAddress(nDeleteCol2, nDeleteRow1-1, nTab)); // 1
            rNewRanges.push_back(aNewRange);

            aNewRange = ScRange(nCol1, nDeleteRow2+1, nTab, nDeleteCol2, nRow2, nTab); // 3
            rNewRanges.push_back(aNewRange);

            r.aStart.SetCol(nDeleteCol2+1); // 2
        }
        return true;
    }
    else if (checkForThreeRanges(nDeleteRow1, nDeleteRow2, nDeleteCol1, nDeleteCol2, nRow1, nRow2, nCol1, nCol2))
    {
        if (nRow1 < nDeleteRow1)
        {
            // +----------+
            // |    1     |
            // +---+--+---+
            // |   |xx|   |
            // | 2 |xx| 3 |
            // |   |xx|   |
            // +---+xx+---+
            //     |xx|
            //     +--+

            ScRange aNewRange(nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nRow2, nTab); // 2
            rNewRanges.push_back( aNewRange );

            aNewRange = ScRange(ScAddress(nDeleteCol2+1, nDeleteRow1, nTab), aPEnd); // 3
            rNewRanges.push_back( aNewRange );

            r.aEnd.SetRow(nDeleteRow1-1); // 1
        }
        else
        {
            //     +--+
            //     |xx|
            // +---+xx+---+
            // | 1 |xx| 2 |
            // |   |xx|   |
            // +---+--+---+
            // |    3     |
            // +----------+

            ScRange aNewRange(aPStart, ScAddress(nDeleteCol1-1, nDeleteRow2, nTab)); // 1
            rNewRanges.push_back(aNewRange);

            aNewRange = ScRange(nDeleteCol2+1, nRow1, nTab, nCol2, nDeleteRow2, nTab); // 2
            rNewRanges.push_back( aNewRange );

            r.aStart.SetRow(nDeleteRow2+1); // 3
        }
        return true;
    }

    return false;
}

bool handleFourRanges( const ScRange& rDelRange, ScRange& r, std::vector<ScRange>& rNewRanges )
{
    const ScAddress& rDelStart = rDelRange.aStart;
    const ScAddress& rDelEnd = rDelRange.aEnd;
    ScAddress aPStart = r.aStart;
    ScAddress aPEnd = r.aEnd;
    SCCOL nDeleteCol1 = rDelStart.Col();
    SCCOL nDeleteCol2 = rDelEnd.Col();
    SCROW nDeleteRow1 = rDelStart.Row();
    SCROW nDeleteRow2 = rDelEnd.Row();
    SCCOL nCol1 = aPStart.Col();
    SCCOL nCol2 = aPEnd.Col();
    SCROW nRow1 = aPStart.Row();
    SCROW nRow2 = aPEnd.Row();
    SCTAB nTab = aPStart.Tab();

    if (nCol1 < nDeleteCol1 && nDeleteCol2 < nCol2 && nRow1 < nDeleteRow1 && nDeleteRow2 < nRow2)
    {

        // +---------------+
        // |       1       |
        // +---+-------+---+
        // |   |xxxxxxx|   |
        // | 2 |xxxxxxx| 3 |
        // |   |xxxxxxx|   |
        // +---+-------+---+
        // |       4       |
        // +---------------+

        ScRange aNewRange(ScAddress(nCol1, nDeleteRow2+1, nTab), aPEnd); // 4
        rNewRanges.push_back( aNewRange );

        aNewRange = ScRange(nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nDeleteRow2, nTab); // 2
        rNewRanges.push_back( aNewRange );

        aNewRange = ScRange(nDeleteCol2+1, nDeleteRow1, nTab, nCol2, nDeleteRow2, nTab); // 3
        rNewRanges.push_back( aNewRange );

        r.aEnd.SetRow(nDeleteRow1-1); // 1

        return true;
    }

    return false;
}

}

bool ScRangeList::DeleteArea( SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
                                SCCOL nCol2, SCROW nRow2, SCTAB nTab2 )
{
    bool bChanged = false;
    ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
    for(size_t i = 0; i < maRanges.size();)
    {
        if(aRange.Contains(maRanges[i]))
        {
            Remove(i);
            bChanged = true;
        }
        else
            ++i;
    }

    std::vector<ScRange> aNewRanges;

    for(auto & rRange : maRanges)
    {
        // we have two basic cases here:
        // 1. Delete area and pRange intersect
        // 2. Delete area and pRange are not intersecting
        // checking for 2 and if true skip this range
        if(!rRange.Intersects(aRange))
            continue;

        // We get between 1 and 4 ranges from the difference of the first with the second

        // X either Col or Row and Y then the opposite
        // r = deleteRange, p = entry from ScRangeList

        // getting exactly one range is the simple case
        // r.aStart.X() <= p.aStart.X() && r.aEnd.X() >= p.aEnd.X()
        // && ( r.aStart.Y() <= p.aStart.Y() || r.aEnd.Y() >= r.aEnd.Y() )
        if(handleOneRange( aRange, rRange ))
        {
            bChanged = true;
            continue;
        }

        // getting two ranges
        // r.aStart.X()
        else if(handleTwoRanges( aRange, rRange, aNewRanges ))
        {
            bChanged = true;
            continue;
        }

        // getting 3 ranges
        // r.aStart.X() > p.aStart.X() && r.aEnd.X() >= p.aEnd.X()
        // && r.aStart.Y() > p.aStart.Y() && r.aEnd.Y() < p.aEnd.Y()
        // or
        // r.aStart.X() <= p.aStart.X() && r.aEnd.X() < p.aEnd.X()
        // && r.aStart.Y() > p.aStart.Y() && r.aEnd.Y() < p.aEnd.Y()
        else if(handleThreeRanges( aRange, rRange, aNewRanges ))
        {
            bChanged = true;
            continue;
        }

        // getting 4 ranges
        // r.aStart.X() > p.aStart.X() && r.aEnd().X() < p.aEnd.X()
        // && r.aStart.Y() > p.aStart.Y() && r.aEnd().Y() < p.aEnd.Y()
        else if(handleFourRanges( aRange, rRange, aNewRanges ))
        {
            bChanged = true;
            continue;
        }
    }
    for(const auto & rRange : aNewRanges)
        Join(rRange);

    return bChanged;
}

const ScRange* ScRangeList::Find( const ScAddress& rAdr ) const
{
    auto itr = find_if(
        maRanges.cbegin(), maRanges.cend(), FindEnclosingRange<ScAddress>(rAdr));
    return itr == maRanges.end() ? nullptr : &*itr;
}

ScRange* ScRangeList::Find( const ScAddress& rAdr )
{
    auto itr = find_if(
        maRanges.begin(), maRanges.end(), FindEnclosingRange<ScAddress>(rAdr));
    return itr == maRanges.end() ? nullptr : &*itr;
}

ScRangeList::ScRangeList() : mnMaxRowUsed(-1) {}

ScRangeList::ScRangeList( const ScRangeList& rList ) :
    SvRefBase(rList),
    maRanges(rList.maRanges),
    mnMaxRowUsed(rList.mnMaxRowUsed)
{
}

ScRangeList::ScRangeList(ScRangeList&& rList) noexcept :
    maRanges(std::move(rList.maRanges)),
    mnMaxRowUsed(rList.mnMaxRowUsed)
{
}

ScRangeList::ScRangeList( const ScRange& rRange ) :
    mnMaxRowUsed(-1)
{
    maRanges.reserve(1);
    push_back(rRange);
}

ScRangeList& ScRangeList::operator=(const ScRangeList& rList)
{
    maRanges = rList.maRanges;
    mnMaxRowUsed = rList.mnMaxRowUsed;
    return *this;
}

ScRangeList& ScRangeList::operator=(ScRangeList&& rList) noexcept
{
    maRanges = std::move(rList.maRanges);
    mnMaxRowUsed = rList.mnMaxRowUsed;
    return *this;
}

bool ScRangeList::Intersects( const ScRange& rRange ) const
{
    return std::any_of(maRanges.begin(), maRanges.end(), FindIntersectingRange<ScRange>(rRange));
}

bool ScRangeList::Contains( const ScRange& rRange ) const
{
    return std::any_of(maRanges.begin(), maRanges.end(), FindEnclosingRange<ScRange>(rRange));
}

sal_uInt64 ScRangeList::GetCellCount() const
{
    CountCells func;
    return std::for_each(maRanges.begin(), maRanges.end(), func).getCellCount();
}

void ScRangeList::Remove(size_t nPos)
{
    if (maRanges.size() <= nPos)
        // Out-of-bound condition.  Bail out.
        return;
    maRanges.erase(maRanges.begin() + nPos);
}

void ScRangeList::RemoveAll()
{
    maRanges.clear();
    mnMaxRowUsed = -1;
}

ScRange ScRangeList::Combine() const
{
    if (maRanges.empty())
        return ScRange();

    auto itr = maRanges.cbegin(), itrEnd = maRanges.cend();
    ScRange aRet = *itr;
    ++itr;
    for (; itr != itrEnd; ++itr)
    {
        const ScRange& r = *itr;
        SCROW nRow1 = r.aStart.Row(), nRow2 = r.aEnd.Row();
        SCCOL nCol1 = r.aStart.Col(), nCol2 = r.aEnd.Col();
        SCTAB nTab1 = r.aStart.Tab(), nTab2 = r.aEnd.Tab();
        if (aRet.aStart.Row() > nRow1)
            aRet.aStart.SetRow(nRow1);
        if (aRet.aStart.Col() > nCol1)
            aRet.aStart.SetCol(nCol1);
        if (aRet.aStart.Tab() > nTab1)
            aRet.aStart.SetTab(nTab1);
        if (aRet.aEnd.Row() < nRow2)
            aRet.aEnd.SetRow(nRow2);
        if (aRet.aEnd.Col() < nCol2)
            aRet.aEnd.SetCol(nCol2);
        if (aRet.aEnd.Tab() < nTab2)
            aRet.aEnd.SetTab(nTab2);
    }
    return aRet;
}

void ScRangeList::push_back(const ScRange & r)
{
    maRanges.push_back(r);
    if (mnMaxRowUsed < r.aEnd.Row())
        mnMaxRowUsed = r.aEnd.Row();
}

void ScRangeList::swap( ScRangeList& r )
{
    maRanges.swap(r.maRanges);
    std::swap(mnMaxRowUsed, r.mnMaxRowUsed);
}

ScAddress ScRangeList::GetTopLeftCorner() const
{
    if(empty())
        return ScAddress();

    ScAddress const * pAddr = &maRanges[0].aStart;
    for(size_t i = 1, n = size(); i < n; ++i)
    {
        if(maRanges[i].aStart < *pAddr)
            pAddr = &maRanges[i].aStart;
    }

    return *pAddr;
}

ScRangeList ScRangeList::GetIntersectedRange(const ScRange& rRange) const
{
    ScRangeList aReturn;
    for(auto& rR : maRanges)
    {
        if(rR.Intersects(rRange))
        {
            SCCOL nColStart1, nColEnd1, nColStart2, nColEnd2;
            SCROW nRowStart1, nRowEnd1, nRowStart2, nRowEnd2;
            SCTAB nTabStart1, nTabEnd1, nTabStart2, nTabEnd2;
            rR.GetVars(nColStart1, nRowStart1, nTabStart1,
                        nColEnd1, nRowEnd1, nTabEnd1);
            rRange.GetVars(nColStart2, nRowStart2, nTabStart2,
                        nColEnd2, nRowEnd2, nTabEnd2);

            ScRange aNewRange(std::max<SCCOL>(nColStart1, nColStart2), std::max<SCROW>(nRowStart1, nRowStart2),
                    std::max<SCTAB>(nTabStart1, nTabStart2), std::min<SCCOL>(nColEnd1, nColEnd2),
                    std::min<SCROW>(nRowEnd1, nRowEnd2), std::min<SCTAB>(nTabEnd1, nTabEnd2));
            aReturn.Join(aNewRange);
        }
    }

    return aReturn;
}

//  ScRangePairList
ScRangePairList::~ScRangePairList()
{
}

void ScRangePairList::Remove(size_t nPos)
{
    if (maPairs.size() <= nPos)
        // Out-of-bound condition.  Bail out.
        return;
    maPairs.erase(maPairs.begin() + nPos);
}

void ScRangePairList::Remove( const ScRangePair & rAdr)
{
    auto itr = std::find_if(maPairs.begin(), maPairs.end(), [&rAdr](const ScRangePair& rPair) { return &rAdr == &rPair; });
    if (itr != maPairs.end())
    {
        maPairs.erase( itr );
        return;
    }
    assert(false);
}

ScRangePair & ScRangePairList::operator [](size_t idx)
{
    return maPairs[idx];
}

const ScRangePair & ScRangePairList::operator [](size_t idx) const
{
    return maPairs[idx];
}

size_t ScRangePairList::size() const
{
    return maPairs.size();
}

void ScRangePairList::UpdateReference( UpdateRefMode eUpdateRefMode,
                                    const ScDocument& rDoc, const ScRange& rWhere,
                                    SCCOL nDx, SCROW nDy, SCTAB nDz )
{
    if ( maPairs.empty() )
        return;

    SCCOL nCol1;
    SCROW nRow1;
    SCTAB nTab1;
    SCCOL nCol2;
    SCROW nRow2;
    SCTAB nTab2;
    rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
    for (ScRangePair & rR : maPairs)
    {
        for ( sal_uInt16 j=0; j<2; j++ )
        {
            ScRange& rRange = rR.GetRange(j);
            SCCOL theCol1;
            SCROW theRow1;
            SCTAB theTab1;
            SCCOL theCol2;
            SCROW theRow2;
            SCTAB theTab2;
            rRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
            if ( ScRefUpdate::Update( rDoc, eUpdateRefMode,
                    nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
                    nDx, nDy, nDz,
                    theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
                    != UR_NOTHING )
            {
                rRange.aStart.Set( theCol1, theRow1, theTab1 );
                rRange.aEnd.Set( theCol2, theRow2, theTab2 );
            }
        }
    }
}

// Delete entries that have the labels (first range) on nTab
void ScRangePairList::DeleteOnTab( SCTAB nTab )
{
    std::erase_if(maPairs,
        [&nTab](const ScRangePair& rR) {
            const ScRange & rRange = rR.GetRange(0);
            return (rRange.aStart.Tab() == nTab) && (rRange.aEnd.Tab() == nTab);
        });
}

ScRangePair* ScRangePairList::Find( const ScAddress& rAdr )
{
    for (ScRangePair & rR : maPairs)
    {
        if ( rR.GetRange(0).Contains( rAdr ) )
            return &rR;
    }
    return nullptr;
}

ScRangePair* ScRangePairList::Find( const ScRange& rRange )
{
    for (ScRangePair & rR : maPairs)
    {
        if ( rR.GetRange(0) == rRange )
            return &rR;
    }
    return nullptr;
}

ScRangePairList* ScRangePairList::Clone() const
{
    ScRangePairList* pNew = new ScRangePairList;
    for (const ScRangePair & rR : maPairs)
    {
        pNew->Append( rR );
    }
    return pNew;
}

namespace {

class ScRangePairList_sortNameCompare
{
public:
    ScRangePairList_sortNameCompare(ScDocument& rDoc) : mrDoc(rDoc) {}

    bool operator()( const ScRangePair *ps1, const ScRangePair* ps2 ) const
    {
        const ScAddress& rStartPos1 = ps1->GetRange(0).aStart;
        const ScAddress& rStartPos2 = ps2->GetRange(0).aStart;
        OUString aStr1, aStr2;
        sal_Int32 nComp;
        if ( rStartPos1.Tab() == rStartPos2.Tab() )
            nComp = 0;
        else
        {
            mrDoc.GetName( rStartPos1.Tab(), aStr1 );
            mrDoc.GetName( rStartPos2.Tab(), aStr2 );
            nComp = ScGlobal::GetCollator().compareString( aStr1, aStr2 );
        }
        if (nComp < 0)
        {
            return true; // -1;
        }
        else if (nComp > 0)
        {
            return false; // 1;
        }

        // equal tabs
        if ( rStartPos1.Col() < rStartPos2.Col() )
            return true; // -1;
        if ( rStartPos1.Col() > rStartPos2.Col() )
            return false; // 1;
        // equal cols
        if ( rStartPos1.Row() < rStartPos2.Row() )
            return true; // -1;
        if ( rStartPos1.Row() > rStartPos2.Row() )
            return false; // 1;

        // first corner equal, second corner
        const ScAddress& rEndPos1 = ps1->GetRange(0).aEnd;
        const ScAddress& rEndPos2 = ps2->GetRange(0).aEnd;
        if ( rEndPos1.Tab() == rEndPos2.Tab() )
            nComp = 0;
        else
        {
            mrDoc.GetName( rEndPos1.Tab(), aStr1 );
            mrDoc.GetName( rEndPos2.Tab(), aStr2 );
            nComp = ScGlobal::GetCollator().compareString( aStr1, aStr2 );
        }
        if (nComp < 0)
        {
            return true; // -1;
        }
        else if (nComp > 0)
        {
            return false; // 1;
        }

        // equal tabs
        if ( rEndPos1.Col() < rEndPos2.Col() )
            return true; // -1;
        if ( rEndPos1.Col() > rEndPos2.Col() )
            return false; // 1;
        // equal cols
        if ( rEndPos1.Row() < rEndPos2.Row() )
            return true; // -1;
        if ( rEndPos1.Row() > rEndPos2.Row() )
            return false; // 1;

        return false;
    }
private:
    ScDocument& mrDoc;
};

}

void ScRangePairList::Join( const ScRangePair& r, bool bIsInList )
{
    if ( maPairs.empty() )
    {
        Append( r );
        return ;
    }

    bool bJoinedInput = false;
    const ScRangePair* pOver = &r;

Label_RangePair_Join:

    assert(pOver);
    const ScRange& r1 = pOver->GetRange(0);
    const ScRange& r2 = pOver->GetRange(1);
    const SCCOL nCol1 = r1.aStart.Col();
    const SCROW nRow1 = r1.aStart.Row();
    const SCTAB nTab1 = r1.aStart.Tab();
    const SCCOL nCol2 = r1.aEnd.Col();
    const SCROW nRow2 = r1.aEnd.Row();
    const SCTAB nTab2 = r1.aEnd.Tab();

    size_t nOverPos = std::numeric_limits<size_t>::max();
    for (size_t i = 0; i < maPairs.size(); ++i)
    {
        ScRangePair & rPair = maPairs[ i ];
        if ( &rPair == pOver )
        {
            nOverPos = i;
            continue;           // the same one, continue with the next
        }
        bool bJoined = false;
        ScRange& rp1 = rPair.GetRange(0);
        ScRange& rp2 = rPair.GetRange(1);
        if ( rp2 == r2 )
        {   // only if Range2 is equal
            if ( rp1.Contains( r1 ) )
            {   // RangePair pOver included in or identical to RangePair p
                if ( bIsInList )
                    bJoined = true;     // do away with RangePair pOver
                else
                {   // that was all then
                    bJoinedInput = true;    // don't append
                    break;  // for
                }
            }
            else if ( r1.Contains( rp1 ) )
            {   // RangePair p included in RangePair pOver, make pOver the new RangePair
                rPair = *pOver;
                bJoined = true;
            }
        }
        if ( !bJoined && rp1.aStart.Tab() == nTab1 && rp1.aEnd.Tab() == nTab2
          && rp2.aStart.Tab() == r2.aStart.Tab()
          && rp2.aEnd.Tab() == r2.aEnd.Tab() )
        {   // 2D, Range2 must be located side-by-side just like Range1
            if ( rp1.aStart.Col() == nCol1 && rp1.aEnd.Col() == nCol2
              && rp2.aStart.Col() == r2.aStart.Col()
              && rp2.aEnd.Col() == r2.aEnd.Col() )
            {
                if ( rp1.aStart.Row() == nRow2+1
                  && rp2.aStart.Row() == r2.aEnd.Row()+1 )
                {   // top
                    rp1.aStart.SetRow( nRow1 );
                    rp2.aStart.SetRow( r2.aStart.Row() );
                    bJoined = true;
                }
                else if ( rp1.aEnd.Row() == nRow1-1
                  && rp2.aEnd.Row() == r2.aStart.Row()-1 )
                {   // bottom
                    rp1.aEnd.SetRow( nRow2 );
                    rp2.aEnd.SetRow( r2.aEnd.Row() );
                    bJoined = true;
                }
            }
            else if ( rp1.aStart.Row() == nRow1 && rp1.aEnd.Row() == nRow2
              && rp2.aStart.Row() == r2.aStart.Row()
              && rp2.aEnd.Row() == r2.aEnd.Row() )
            {
                if ( rp1.aStart.Col() == nCol2+1
                  && rp2.aStart.Col() == r2.aEnd.Col()+1 )
                {   // left
                    rp1.aStart.SetCol( nCol1 );
                    rp2.aStart.SetCol( r2.aStart.Col() );
                    bJoined = true;
                }
                else if ( rp1.aEnd.Col() == nCol1-1
                  && rp2.aEnd.Col() == r2.aEnd.Col()-1 )
                {   // right
                    rp1.aEnd.SetCol( nCol2 );
                    rp2.aEnd.SetCol( r2.aEnd.Col() );
                    bJoined = true;
                }
            }
        }
        if ( bJoined )
        {
            if ( bIsInList )
            {   // delete RangePair pOver within the list
                if (nOverPos != std::numeric_limits<size_t>::max())
                {
                    Remove(nOverPos);
                    if (nOverPos < i)
                        --i;
                }
                else
                {
                    for (size_t nOver = 0, nRangePairs = maPairs.size(); nOver < nRangePairs; ++nOver)
                    {
                        if (&maPairs[nOver] == pOver)
                        {
                            maPairs.erase(maPairs.begin() + nOver);
                            break;
                        }
                    }
                    assert(false);
                }
            }
            bJoinedInput = true;
            pOver = &maPairs[i];
            bIsInList = true;
            goto Label_RangePair_Join;
        }
    }
    if ( !bIsInList && !bJoinedInput )
        Append( r );
}

std::vector<const ScRangePair*> ScRangePairList::CreateNameSortedArray( ScDocument& rDoc ) const
{
    std::vector<const ScRangePair*> aSortedVec(maPairs.size());
    size_t i = 0;
    for ( auto const & rPair : maPairs)
    {
        aSortedVec[i++] = &rPair;
    }

    std::sort( aSortedVec.begin(), aSortedVec.end(), ScRangePairList_sortNameCompare(rDoc) );

    return aSortedVec;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
