/* -*- 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 <functional>
 
#include <string.h>
#include <osl/diagnose.h>
#include <sal/log.hxx>
 
#include <token.hxx>
#include <tokenarray.hxx>
#include <reftokenhelper.hxx>
#include <clipparam.hxx>
#include <compiler.hxx>
#include <interpre.hxx>
#include <formula/FormulaCompiler.hxx>
#include <formula/compiler.hxx>
#include <formula/opcode.hxx>
#include <jumpmatrix.hxx>
#include <rangeseq.hxx>
#include <rangeutl.hxx>
#include <externalrefmgr.hxx>
#include <document.hxx>
#include <refupdatecontext.hxx>
#include <tokenstringcontext.hxx>
#include <types.hxx>
#include <addincol.hxx>
#include <dbdata.hxx>
#include <reordermap.hxx>
#include <svl/sharedstring.hxx>
#include <scmatrix.hxx>
 
#include <com/sun/star/sheet/ComplexReference.hpp>
#include <com/sun/star/sheet/ExternalReference.hpp>
#include <com/sun/star/sheet/FormulaToken.hpp>
#include <com/sun/star/sheet/ReferenceFlags.hpp>
#include <com/sun/star/sheet/NameToken.hpp>
#include <com/sun/star/sheet/TableRefToken.hpp>
#include <utility>
#include <o3tl/safeint.hxx>
#include <o3tl/sorted_vector.hxx>
 
using ::std::vector;
using namespace formula;
using namespace com::sun::star;
 
namespace
{
    void lcl_SingleRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
    {
        rRef.InitFlags();
 
        rRef.SetColRel(     ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
        rRef.SetRowRel(     ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE    ) != 0 );
        rRef.SetTabRel(     ( rAPI.Flags & sheet::ReferenceFlags::SHEET_RELATIVE  ) != 0 );
        rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED  ) != 0 );
        rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED     ) != 0 );
        rRef.SetTabDeleted( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_DELETED   ) != 0 );
        rRef.SetFlag3D(     ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D        ) != 0 );
        rRef.SetRelName(    ( rAPI.Flags & sheet::ReferenceFlags::RELATIVE_NAME   ) != 0 );
 
        if (rRef.IsColRel())
            rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
        else
            rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
 
        if (rRef.IsRowRel())
            rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
        else
            rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
 
        if (rRef.IsTabRel())
            rRef.SetRelTab(static_cast<SCTAB>(rAPI.RelativeSheet));
        else
            rRef.SetAbsTab(static_cast<SCTAB>(rAPI.Sheet));
    }
 
    void lcl_ExternalRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
    {
        rRef.InitFlags();
 
        rRef.SetColRel(     ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
        rRef.SetRowRel(     ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE    ) != 0 );
        rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED  ) != 0 );
        rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED     ) != 0 );
        rRef.SetTabDeleted( false );    // sheet must not be deleted for external refs
        rRef.SetFlag3D(     ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D        ) != 0 );
        rRef.SetRelName(    false );
 
        if (rRef.IsColRel())
            rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
        else
            rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
 
        if (rRef.IsRowRel())
            rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
        else
            rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
 
        // sheet index must be absolute for external refs
        rRef.SetAbsTab(0);
    }
 
    struct TokenPointerRange
    {
        FormulaToken**  mpStart;
        FormulaToken**  mpStop;
 
        TokenPointerRange() : mpStart(nullptr), mpStop(nullptr) {}
        TokenPointerRange( FormulaToken** p, sal_uInt16 n ) :
            mpStart(p), mpStop( p + static_cast<size_t>(n)) {}
    };
    struct TokenPointers
    {
        TokenPointerRange maPointerRange[2];
        bool              mbSkipRelName;
 
        TokenPointers( FormulaToken** pCode, sal_uInt16 nLen, FormulaToken** pRPN, sal_uInt16 nRPN,
                bool bSkipRelName = true ) :
            mbSkipRelName(bSkipRelName)
        {
            maPointerRange[0] = TokenPointerRange( pCode, nLen);
            maPointerRange[1] = TokenPointerRange( pRPN, nRPN);
        }
 
        bool skipToken( size_t i, const FormulaToken* const * pp )
        {
            // Handle all code tokens, and tokens in RPN only if they have a
            // reference count of 1, which means they are not referenced in the
            // code array. Doing it the other way would skip code tokens that
            // are held by flat copied token arrays and thus are shared. For
            // flat copy arrays the caller has to know what it does and should
            // discard all RPN, update only one array and regenerate all RPN.
            if (i == 1)
            {
                if ((*pp)->GetRef() > 1)
                    return true;
 
                if (mbSkipRelName)
                {
                    // Skip (do not adjust) relative references resulting from
                    // named expressions. Resolved expressions are only in RPN.
                    switch ((*pp)->GetType())
                    {
                        case svSingleRef:
                            return (*pp)->GetSingleRef()->IsRelName();
                        case svDoubleRef:
                            {
                                const ScComplexRefData& rRef = *(*pp)->GetDoubleRef();
                                return rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName();
                            }
                        default:
                            ;   // nothing
                    }
                }
            }
 
            return false;
        }
 
        FormulaToken* getHandledToken( size_t i, FormulaToken* const * pp )
        {
            if (skipToken( i, pp))
                return nullptr;
 
            FormulaToken* p = *pp;
            if (p->GetOpCode() == ocTableRef)
            {
                // Return the inner reference token if it is not in RPN.
                ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(p);
                if (!pTR)
                    return p;
                p = pTR->GetAreaRefRPN();
                if (!p)
                    return pTR;
                if (p->GetRef() > 1)
                    // Reference handled in RPN, but do not return nullptr so
                    // loops will process ocTableRef via pp instead of issuing
                    // a continue.
                    return pTR;
            }
            return p;
        }
    };
 
} // namespace
 
 
// --- class ScRawToken -----------------------------------------------------
 
void ScRawToken::SetOpCode( OpCode e )
{
    eOp   = e;
    switch (eOp)
    {
        case ocIf:
            eType = svJump;
            nJump[ 0 ] = 3; // If, Else, Behind
            break;
        case ocIfError:
        case ocIfNA:
            eType = svJump;
            nJump[ 0 ] = 2; // If, Behind
            break;
        case ocChoose:
            eType = svJump;
            nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
            break;
        case ocLet:
            eType = svJump;
            nJump[ 0 ] = FORMULA_MAXPARAMS + 1;
            break;
        case ocMissing:
            eType = svMissing;
            break;
        case ocSep:
        case ocOpen:
        case ocClose:
        case ocArrayRowSep:
        case ocArrayColSep:
        case ocArrayOpen:
        case ocArrayClose:
        case ocTableRefOpen:
        case ocTableRefClose:
            eType = svSep;
            break;
        case ocWhitespace:
            eType = svByte;
            whitespace.nCount = 1;
            whitespace.cChar = 0x20;
            break;
        default:
            eType = svByte;
            sbyte.cByte = 0;
            sbyte.eInForceArray = ParamClass::Unknown;
    }
}
 
void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
{
    eOp   = ocPush;
    eType = svString;
 
    sharedstring.mpData = pData;
    sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
}
 
void ScRawToken::SetStringName( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
{
    eOp = ocStringName;
    eType = svString;
 
    sharedstring.mpData = pData;
    sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
}
 
void ScRawToken::SetSingleReference( const ScSingleRefData& rRef )
{
    eOp       = ocPush;
    eType     = svSingleRef;
    aRef.Ref1 =
    aRef.Ref2 = rRef;
}
 
void ScRawToken::SetDoubleReference( const ScComplexRefData& rRef )
{
    eOp   = ocPush;
    eType = svDoubleRef;
    aRef  = rRef;
}
 
void ScRawToken::SetDouble(double rVal)
{
    eOp   = ocPush;
    eType = svDouble;
    nValue = rVal;
}
 
void ScRawToken::SetErrorConstant( FormulaError nErr )
{
    eOp   = ocPush;
    eType = svError;
    nError = nErr;
}
 
void ScRawToken::SetName(sal_Int16 nSheet, sal_uInt16 nIndex)
{
    eOp = ocName;
    eType = svIndex;
 
    name.nSheet = nSheet;
    name.nIndex = nIndex;
}
 
void ScRawToken::SetExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
{
    eOp = ocPush;
    eType = svExternalSingleRef;
 
    extref.nFileId = nFileId;
    extref.aRef.Ref1 =
    extref.aRef.Ref2 = rRef;
    maExternalName = rTabName;
}
 
void ScRawToken::SetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
{
    eOp = ocPush;
    eType = svExternalDoubleRef;
 
    extref.nFileId = nFileId;
    extref.aRef = rRef;
    maExternalName = rTabName;
}
 
void ScRawToken::SetExternalName( sal_uInt16 nFileId, const OUString& rName )
{
    eOp = ocPush;
    eType = svExternalName;
 
    extname.nFileId = nFileId;
    maExternalName = rName;
}
 
void ScRawToken::SetExternal( const OUString& rStr )
{
    eOp   = ocExternal;
    eType = svExternal;
    maExternalName = rStr;
}
 
bool ScRawToken::IsValidReference(const ScDocument& rDoc) const
{
    switch (eType)
    {
        case svSingleRef:
            return aRef.Ref1.Valid(rDoc);
        case svDoubleRef:
            return aRef.Valid(rDoc);
        case svExternalSingleRef:
        case svExternalDoubleRef:
            return true;
        default:
            ;   // nothing
    }
    return false;
}
 
FormulaToken* ScRawToken::CreateToken(ScSheetLimits& rLimits) const
{
#define IF_NOT_OPCODE_ERROR(o,c) SAL_WARN_IF((eOp!=o), "sc.core", #c "::ctor: OpCode " << static_cast<int>(eOp) << " lost, converted to " #o "; maybe inherit from FormulaToken instead!")
    switch ( GetType() )
    {
        case svByte :
            if (eOp == ocWhitespace)
                return new FormulaSpaceToken( whitespace.nCount, whitespace.cChar );
            else
                return new FormulaByteToken( eOp, sbyte.cByte, sbyte.eInForceArray );
        case svDouble :
            IF_NOT_OPCODE_ERROR( ocPush, FormulaDoubleToken);
            return new FormulaDoubleToken( nValue );
        case svString :
        {
            svl::SharedString aSS(sharedstring.mpData, sharedstring.mpDataIgnoreCase);
            if (eOp == ocPush)
                return new FormulaStringToken(std::move(aSS));
            else
                return new FormulaStringOpToken(eOp, std::move(aSS));
        }
        case svSingleRef :
            return new ScSingleRefToken(rLimits, aRef.Ref1, eOp);
        case svDoubleRef :
            return new ScDoubleRefToken(rLimits, aRef, eOp);
        case svMatrix :
            IF_NOT_OPCODE_ERROR( ocPush, ScMatrixToken);
            return new ScMatrixToken( pMat );
        case svIndex :
            if (eOp == ocTableRef)
                return new ScTableRefToken( table.nIndex, table.eItem);
            else
                return new FormulaIndexToken( eOp, name.nIndex, name.nSheet);
        case svExternalSingleRef:
            {
                svl::SharedString aTabName(maExternalName);    // string not interned
                return new ScExternalSingleRefToken(extref.nFileId, std::move(aTabName), extref.aRef.Ref1);
            }
        case svExternalDoubleRef:
            {
                svl::SharedString aTabName(maExternalName);    // string not interned
                return new ScExternalDoubleRefToken(extref.nFileId, std::move(aTabName), extref.aRef);
            }
        case svExternalName:
            {
                svl::SharedString aName(maExternalName);         // string not interned
                return new ScExternalNameToken( extname.nFileId, std::move(aName) );
            }
        case svJump :
            return new FormulaJumpToken( eOp, nJump );
        case svExternal :
            return new FormulaExternalToken( eOp, sbyte.cByte, maExternalName );
        case svFAP :
            return new FormulaFAPToken( eOp, sbyte.cByte, nullptr );
        case svMissing :
            IF_NOT_OPCODE_ERROR( ocMissing, FormulaMissingToken);
            return new FormulaMissingToken;
        case svSep :
            return new FormulaToken( svSep,eOp );
        case svError :
            return new FormulaErrorToken( nError );
        case svUnknown :
            return new FormulaUnknownToken( eOp );
        default:
            {
                SAL_WARN("sc.core",  "unknown ScRawToken::CreateToken() type " << int(GetType()));
                return new FormulaUnknownToken( ocBad );
            }
    }
#undef IF_NOT_OPCODE_ERROR
}
 
namespace {
 
//  TextEqual: if same formula entered (for optimization in sort)
bool checkTextEqual( const ScSheetLimits& rLimits, const FormulaToken& _rToken1, const FormulaToken& _rToken2 )
{
    assert(
        (_rToken1.GetType() == svSingleRef || _rToken1.GetType() == svDoubleRef)
        && _rToken1.FormulaToken::operator ==(_rToken2));
 
    //  in relative Refs only compare relative parts
 
    ScComplexRefData aTemp1;
    if ( _rToken1.GetType() == svSingleRef )
    {
        aTemp1.Ref1 = *_rToken1.GetSingleRef();
        aTemp1.Ref2 = aTemp1.Ref1;
    }
    else
        aTemp1 = *_rToken1.GetDoubleRef();
 
    ScComplexRefData aTemp2;
    if ( _rToken2.GetType() == svSingleRef )
    {
        aTemp2.Ref1 = *_rToken2.GetSingleRef();
        aTemp2.Ref2 = aTemp2.Ref1;
    }
    else
        aTemp2 = *_rToken2.GetDoubleRef();
 
    ScAddress aPos;
    ScRange aRange1 = aTemp1.toAbs(rLimits, aPos), aRange2 = aTemp2.toAbs(rLimits, aPos);
 
    //  memcmp doesn't work because of the alignment byte after bFlags.
    //  After SmartRelAbs only absolute parts have to be compared.
    return aRange1 == aRange2 && aTemp1.Ref1.FlagValue() == aTemp2.Ref1.FlagValue() && aTemp1.Ref2.FlagValue() == aTemp2.Ref2.FlagValue();
}
 
}
 
#if DEBUG_FORMULA_COMPILER
void DumpToken(formula::FormulaToken const & rToken)
{
    switch (rToken.GetType()) {
    case svSingleRef:
        cout << "-- ScSingleRefToken" << endl;
        rToken.GetSingleRef()->Dump(1);
        break;
    case svDoubleRef:
        cout << "-- ScDoubleRefToken" << endl;
        rToken.GetDoubleRef()->Dump(1);
        break;
    default:
        cout << "-- FormulaToken" << endl;
        cout << "  opcode: " << int(rToken.GetOpCode()) << " " <<
            formula::FormulaCompiler::GetNativeSymbol( rToken.GetOpCode()).toUtf8().getStr() << endl;
        cout << "  type: " << static_cast<int>(rToken.GetType()) << endl;
        switch (rToken.GetType())
        {
        case svDouble:
            cout << "  value: " << rToken.GetDouble() << endl;
            break;
        case svString:
            cout << "  string: "
                 << OUStringToOString(rToken.GetString().getString(), RTL_TEXTENCODING_UTF8).getStr()
                 << endl;
            break;
        default:
            ;
        }
        break;
    }
}
#endif
 
FormulaTokenRef extendRangeReference( ScSheetLimits& rLimits, FormulaToken & rTok1, FormulaToken & rTok2,
        const ScAddress & rPos, bool bReuseDoubleRef )
{
 
    StackVar sv1 = rTok1.GetType();
    // Doing a RangeOp with RefList is probably utter nonsense, but Xcl
    // supports it, so do we.
    if (sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList
         && sv1 != svExternalSingleRef && sv1 != svExternalDoubleRef)
        return nullptr;
    StackVar sv2 = rTok2.GetType();
    if (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)
        return nullptr;
 
    ScTokenRef xRes;
    bool bExternal = (sv1 == svExternalSingleRef);
    if ((sv1 == svSingleRef || bExternal) && sv2 == svSingleRef)
    {
        // Range references like Sheet1.A1:A2 are generalized and built by
        // first creating a DoubleRef from the first SingleRef, effectively
        // generating Sheet1.A1:A1, and then extending that with A2 as if
        // Sheet1.A1:A1:A2 was encountered, so the mechanisms to adjust the
        // references apply as well.
 
        /* Given the current structure of external references an external
         * reference can only be extended if the second reference does not
         * point to a different sheet. 'file'#Sheet1.A1:A2 is ok,
         * 'file'#Sheet1.A1:Sheet2.A2 is not. Since we can't determine from a
         * svSingleRef whether the sheet would be different from the one given
         * in the external reference, we have to bail out if there is any sheet
         * specified. NOTE: Xcl does handle external 3D references as in
         * '[file]Sheet1:Sheet2'!A1:A2
         *
         * FIXME: For OOo syntax be smart and remember an external singleref
         * encountered and if followed by ocRange and singleref, create an
         * external singleref for the second singleref. Both could then be
         * merged here. For Xcl syntax already parse an external range
         * reference entirely, cumbersome. */
 
        const ScSingleRefData& rRef2 = *rTok2.GetSingleRef();
        if (bExternal && rRef2.IsFlag3D())
            return nullptr;
 
        ScComplexRefData aRef;
        aRef.Ref1 = aRef.Ref2 = *rTok1.GetSingleRef();
        aRef.Ref2.SetFlag3D( false);
        aRef.Extend(rLimits, rRef2, rPos);
        if (bExternal)
            xRes = new ScExternalDoubleRefToken( rTok1.GetIndex(), rTok1.GetString(), aRef);
        else
            xRes = new ScDoubleRefToken(rLimits, aRef);
    }
    else
    {
        bExternal |= (sv1 == svExternalDoubleRef);
        const ScRefList* pRefList = nullptr;
        if (sv1 == svDoubleRef)
        {
            xRes = (bReuseDoubleRef && rTok1.GetRef() == 1 ? &rTok1 : rTok1.Clone());
            sv1 = svUnknown;    // mark as handled
        }
        else if (sv2 == svDoubleRef)
        {
            xRes = (bReuseDoubleRef && rTok2.GetRef() == 1 ? &rTok2 : rTok2.Clone());
            sv2 = svUnknown;    // mark as handled
        }
        else if (sv1 == svRefList)
            pRefList = rTok1.GetRefList();
        else if (sv2 == svRefList)
            pRefList = rTok2.GetRefList();
        if (pRefList)
        {
            if (pRefList->empty())
                return nullptr;
            if (bExternal)
                return nullptr;    // external reference list not possible
            xRes = new ScDoubleRefToken(rLimits, (*pRefList)[0] );
        }
        if (!xRes)
            return nullptr;    // shouldn't happen...
        StackVar sv[2] = { sv1, sv2 };
        formula::FormulaToken* pt[2] = { &rTok1, &rTok2 };
        ScComplexRefData& rRef = *xRes->GetDoubleRef();
        for (size_t i=0; i<2; ++i)
        {
            switch (sv[i])
            {
                case svSingleRef:
                    rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
                    break;
                case svDoubleRef:
                    rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
                    break;
                case svRefList:
                    {
                        const ScRefList* p = pt[i]->GetRefList();
                        if (p->empty())
                            return nullptr;
                        for (const auto& rRefData : *p)
                        {
                            rRef.Extend(rLimits, rRefData, rPos);
                        }
                    }
                    break;
                case svExternalSingleRef:
                    if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
                        return nullptr;    // no other sheets with external refs
                    else
                        rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
                    break;
                case svExternalDoubleRef:
                    if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
                        return nullptr;    // no other sheets with external refs
                    else
                        rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
                    break;
                default:
                    ;   // nothing, prevent compiler warning
            }
        }
    }
    return FormulaTokenRef(xRes.get());
}
 
// real implementations of virtual functions
 
const ScSingleRefData*    ScSingleRefToken::GetSingleRef() const  { return &aSingleRef; }
ScSingleRefData*          ScSingleRefToken::GetSingleRef()        { return &aSingleRef; }
bool ScSingleRefToken::TextEqual( const FormulaToken& _rToken ) const
{
    return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
}
bool ScSingleRefToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && aSingleRef == *r.GetSingleRef();
}
 
const ScSingleRefData*    ScDoubleRefToken::GetSingleRef() const  { return &aDoubleRef.Ref1; }
ScSingleRefData*          ScDoubleRefToken::GetSingleRef()        { return &aDoubleRef.Ref1; }
const ScComplexRefData*     ScDoubleRefToken::GetDoubleRef() const  { return &aDoubleRef; }
ScComplexRefData*           ScDoubleRefToken::GetDoubleRef()        { return &aDoubleRef; }
const ScSingleRefData*    ScDoubleRefToken::GetSingleRef2() const { return &aDoubleRef.Ref2; }
ScSingleRefData*          ScDoubleRefToken::GetSingleRef2()       { return &aDoubleRef.Ref2; }
bool ScDoubleRefToken::TextEqual( const FormulaToken& _rToken ) const
{
    return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
}
bool ScDoubleRefToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && aDoubleRef == *r.GetDoubleRef();
}
 
const ScRefList*        ScRefListToken::GetRefList() const  { return &aRefList; }
      ScRefList*        ScRefListToken::GetRefList()        { return &aRefList; }
      bool              ScRefListToken::IsArrayResult() const { return mbArrayResult; }
bool ScRefListToken::operator==( const FormulaToken& r ) const
{
    if (!FormulaToken::operator==( r ) || &aRefList != r.GetRefList())
        return false;
    const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(&r);
    return p && mbArrayResult == p->IsArrayResult();
}
 
ScMatrixToken::ScMatrixToken( ScMatrixRef p ) :
    FormulaToken(formula::svMatrix), pMatrix(std::move(p)) {}
 
ScMatrixToken::ScMatrixToken( const ScMatrixToken& ) = default;
 
const ScMatrix* ScMatrixToken::GetMatrix() const        { return pMatrix.get(); }
ScMatrix*       ScMatrixToken::GetMatrix()              { return pMatrix.get(); }
bool ScMatrixToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && pMatrix == r.GetMatrix();
}
 
ScMatrixRangeToken::ScMatrixRangeToken( const sc::RangeMatrix& rMat ) :
    FormulaToken(formula::svMatrix), mpMatrix(rMat.mpMat)
{
    maRef.InitRange(rMat.mnCol1, rMat.mnRow1, rMat.mnTab1, rMat.mnCol2, rMat.mnRow2, rMat.mnTab2);
}
 
ScMatrixRangeToken::ScMatrixRangeToken( const ScMatrixRangeToken& ) = default;
 
sal_uInt8 ScMatrixRangeToken::GetByte() const
{
    return MATRIX_TOKEN_HAS_RANGE;
}
 
const ScMatrix* ScMatrixRangeToken::GetMatrix() const
{
    return mpMatrix.get();
}
 
ScMatrix* ScMatrixRangeToken::GetMatrix()
{
    return mpMatrix.get();
}
 
const ScComplexRefData* ScMatrixRangeToken::GetDoubleRef() const
{
    return &maRef;
}
 
ScComplexRefData* ScMatrixRangeToken::GetDoubleRef()
{
    return &maRef;
}
 
bool ScMatrixRangeToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==(r) && mpMatrix == r.GetMatrix();
}
 
FormulaToken* ScMatrixRangeToken::Clone() const
{
    return new ScMatrixRangeToken(*this);
}
 
ScExternalSingleRefToken::ScExternalSingleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScSingleRefData& r ) :
    FormulaToken( svExternalSingleRef, ocPush),
    mnFileId(nFileId),
    maTabName(std::move(aTabName)),
    maSingleRef(r)
{
}
 
ScExternalSingleRefToken::~ScExternalSingleRefToken()
{
}
 
sal_uInt16 ScExternalSingleRefToken::GetIndex() const
{
    return mnFileId;
}
 
const svl::SharedString & ScExternalSingleRefToken::GetString() const
{
    return maTabName;
}
 
const ScSingleRefData* ScExternalSingleRefToken::GetSingleRef() const
{
    return &maSingleRef;
}
 
ScSingleRefData* ScExternalSingleRefToken::GetSingleRef()
{
    return &maSingleRef;
}
 
bool ScExternalSingleRefToken::operator ==( const FormulaToken& r ) const
{
    if (!FormulaToken::operator==(r))
        return false;
 
    if (mnFileId != r.GetIndex())
        return false;
 
    if (maTabName != r.GetString())
        return false;
 
    return maSingleRef == *r.GetSingleRef();
}
 
ScExternalDoubleRefToken::ScExternalDoubleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScComplexRefData& r ) :
    FormulaToken( svExternalDoubleRef, ocPush),
    mnFileId(nFileId),
    maTabName(std::move(aTabName)),
    maDoubleRef(r)
{
}
 
ScExternalDoubleRefToken::~ScExternalDoubleRefToken()
{
}
 
sal_uInt16 ScExternalDoubleRefToken::GetIndex() const
{
    return mnFileId;
}
 
const svl::SharedString & ScExternalDoubleRefToken::GetString() const
{
    return maTabName;
}
 
const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef() const
{
    return &maDoubleRef.Ref1;
}
 
ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef()
{
    return &maDoubleRef.Ref1;
}
 
const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2() const
{
    return &maDoubleRef.Ref2;
}
 
ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2()
{
    return &maDoubleRef.Ref2;
}
 
const ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef() const
{
    return &maDoubleRef;
}
 
ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef()
{
    return &maDoubleRef;
}
 
bool ScExternalDoubleRefToken::operator ==( const FormulaToken& r ) const
{
    if (!FormulaToken::operator==(r))
        return false;
 
    if (mnFileId != r.GetIndex())
        return false;
 
    if (maTabName != r.GetString())
        return false;
 
    return maDoubleRef == *r.GetDoubleRef();
}
 
ScExternalNameToken::ScExternalNameToken( sal_uInt16 nFileId, svl::SharedString aName ) :
    FormulaToken( svExternalName, ocPush),
    mnFileId(nFileId),
    maName(std::move(aName))
{
}
 
ScExternalNameToken::~ScExternalNameToken() {}
 
sal_uInt16 ScExternalNameToken::GetIndex() const
{
    return mnFileId;
}
 
const svl::SharedString & ScExternalNameToken::GetString() const
{
    return maName;
}
 
bool ScExternalNameToken::operator==( const FormulaToken& r ) const
{
    if ( !FormulaToken::operator==(r) )
        return false;
 
    if (mnFileId != r.GetIndex())
        return false;
 
    return maName == r.GetString();
}
 
ScTableRefToken::ScTableRefToken( sal_uInt16 nIndex, ScTableRefToken::Item eItem ) :
    FormulaToken( svIndex, ocTableRef),
    mnIndex(nIndex),
    meItem(eItem)
{
}
 
ScTableRefToken::ScTableRefToken( const ScTableRefToken& r ) :
    FormulaToken(r),
    mxAreaRefRPN( r.mxAreaRefRPN ?  r.mxAreaRefRPN->Clone() : nullptr),
    mnIndex(r.mnIndex),
    meItem(r.meItem)
{
}
 
ScTableRefToken::~ScTableRefToken() {}
 
sal_uInt16 ScTableRefToken::GetIndex() const
{
    return mnIndex;
}
 
void ScTableRefToken::SetIndex( sal_uInt16 n )
{
    mnIndex = n;
}
 
sal_Int16 ScTableRefToken::GetSheet() const
{
    // Code asking for this may have to be adapted as it might assume an
    // svIndex token would always be ocName or ocDBArea.
    SAL_WARN("sc.core","ScTableRefToken::GetSheet - maybe adapt caller to know about TableRef?");
    // Database range is always global.
    return -1;
}
 
ScTableRefToken::Item ScTableRefToken::GetItem() const
{
    return meItem;
}
 
void ScTableRefToken::AddItem( ScTableRefToken::Item eItem )
{
    meItem = static_cast<ScTableRefToken::Item>(meItem | eItem);
}
 
void ScTableRefToken::SetAreaRefRPN( formula::FormulaToken* pToken )
{
    mxAreaRefRPN = pToken;
}
 
formula::FormulaToken* ScTableRefToken::GetAreaRefRPN() const
{
    return mxAreaRefRPN.get();
}
 
bool ScTableRefToken::operator==( const FormulaToken& r ) const
{
    if ( !FormulaToken::operator==(r) )
        return false;
 
    if (mnIndex != r.GetIndex())
        return false;
 
    const ScTableRefToken* p = dynamic_cast<const ScTableRefToken*>(&r);
    if (!p)
        return false;
 
    if (meItem != p->GetItem())
        return false;
 
    if (!mxAreaRefRPN && !p->mxAreaRefRPN)
        ;   // nothing
    else if (!mxAreaRefRPN || !p->mxAreaRefRPN)
        return false;
    else if (!(*mxAreaRefRPN == *(p->mxAreaRefRPN)))
        return false;
 
    return true;
}
 
ScJumpMatrixToken::ScJumpMatrixToken(std::shared_ptr<ScJumpMatrix> p)
    : FormulaToken(formula::svJumpMatrix)
    , mpJumpMatrix(std::move(p))
{}
 
ScJumpMatrixToken::ScJumpMatrixToken( const ScJumpMatrixToken & ) = default;
 
ScJumpMatrix* ScJumpMatrixToken::GetJumpMatrix() const
{
    return mpJumpMatrix.get();
}
 
bool ScJumpMatrixToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && mpJumpMatrix.get() == r.GetJumpMatrix();
}
 
ScJumpMatrixToken::~ScJumpMatrixToken()
{
}
 
double          ScEmptyCellToken::GetDouble() const     { return 0.0; }
 
const svl::SharedString & ScEmptyCellToken::GetString() const
{
    return svl::SharedString::getEmptyString();
}
 
bool ScEmptyCellToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) &&
        bInherited == static_cast< const ScEmptyCellToken & >(r).IsInherited() &&
        bDisplayedAsString == static_cast< const ScEmptyCellToken & >(r).IsDisplayedAsString();
}
 
ScMatrixCellResultToken::ScMatrixCellResultToken( ScConstMatrixRef pMat, const formula::FormulaToken* pUL ) :
    FormulaToken(formula::svMatrixCell), xMatrix(std::move(pMat)), xUpperLeft(pUL) {}
 
ScMatrixCellResultToken::ScMatrixCellResultToken( const ScMatrixCellResultToken& ) = default;
 
double          ScMatrixCellResultToken::GetDouble() const  { return xUpperLeft->GetDouble(); }
 
ScMatrixCellResultToken::~ScMatrixCellResultToken() {}
 
const svl::SharedString & ScMatrixCellResultToken::GetString() const
{
    return xUpperLeft->GetString();
}
 
const ScMatrix* ScMatrixCellResultToken::GetMatrix() const  { return xMatrix.get(); }
// Non-const GetMatrix() is private and unused but must be implemented to
// satisfy vtable linkage.
ScMatrix* ScMatrixCellResultToken::GetMatrix()
{
    return const_cast<ScMatrix*>(xMatrix.get());
}
 
bool ScMatrixCellResultToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) &&
        xUpperLeft == static_cast<const ScMatrixCellResultToken &>(r).xUpperLeft &&
        xMatrix == static_cast<const ScMatrixCellResultToken &>(r).xMatrix;
}
 
FormulaToken* ScMatrixCellResultToken::Clone() const
{
    return new ScMatrixCellResultToken(*this);
}
 
void ScMatrixCellResultToken::Assign( const ScMatrixCellResultToken & r )
{
    xMatrix = r.xMatrix;
    xUpperLeft = r.xUpperLeft;
}
 
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken(
    SCCOL nC, SCROW nR, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) :
    ScMatrixCellResultToken(pMat, pUL), nRows(nR), nCols(nC)
{
    CloneUpperLeftIfNecessary();
}
 
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( SCCOL nC, SCROW nR ) :
    ScMatrixCellResultToken(nullptr, nullptr), nRows(nR), nCols(nC) {}
 
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( const ScMatrixFormulaCellToken& r ) :
    ScMatrixCellResultToken(r), nRows(r.nRows), nCols(r.nCols)
{
    CloneUpperLeftIfNecessary();
}
 
ScMatrixFormulaCellToken::~ScMatrixFormulaCellToken() {}
 
bool ScMatrixFormulaCellToken::operator==( const FormulaToken& r ) const
{
    const ScMatrixFormulaCellToken* p = dynamic_cast<const ScMatrixFormulaCellToken*>(&r);
    return p && ScMatrixCellResultToken::operator==( r ) &&
        nCols == p->nCols && nRows == p->nRows;
}
 
void ScMatrixFormulaCellToken::CloneUpperLeftIfNecessary()
{
    if (xUpperLeft && xUpperLeft->GetType() == svDouble)
        xUpperLeft = xUpperLeft->Clone();
}
 
void ScMatrixFormulaCellToken::Assign( const ScMatrixCellResultToken & r )
{
    ScMatrixCellResultToken::Assign( r);
 
    CloneUpperLeftIfNecessary();
}
 
void ScMatrixFormulaCellToken::Assign( const formula::FormulaToken& r )
{
    if (this == &r)
        return;
    const ScMatrixCellResultToken* p = dynamic_cast<const ScMatrixCellResultToken*>(&r);
    if (p)
        ScMatrixCellResultToken::Assign( *p);
    else
    {
        OSL_ENSURE( r.GetType() != svMatrix, "ScMatrixFormulaCellToken::operator=: assigning ScMatrixToken to ScMatrixFormulaCellToken is not proper, use ScMatrixCellResultToken instead");
        if (r.GetType() == svMatrix)
        {
            xUpperLeft = nullptr;
            xMatrix = r.GetMatrix();
        }
        else
        {
            xUpperLeft = &r;
            xMatrix = nullptr;
            CloneUpperLeftIfNecessary();
        }
    }
}
 
void ScMatrixFormulaCellToken::SetUpperLeftDouble( double f )
{
    switch (GetUpperLeftType())
    {
        case svDouble:
            const_cast<FormulaToken*>(xUpperLeft.get())->SetDouble(f);
            break;
        case svString:
            xUpperLeft = new FormulaDoubleToken( f);
            break;
        case svUnknown:
            if (!xUpperLeft)
            {
                xUpperLeft = new FormulaDoubleToken( f);
                break;
            }
            [[fallthrough]];
        default:
            {
                OSL_FAIL("ScMatrixFormulaCellToken::SetUpperLeftDouble: not modifying unhandled token type");
            }
    }
}
 
void ScMatrixFormulaCellToken::ResetResult()
{
    xMatrix = nullptr;
    xUpperLeft = nullptr;
}
 
ScHybridCellToken::ScHybridCellToken(
    double f, const svl::SharedString & rStr, OUString aFormula, bool bEmptyDisplayedAsString ) :
        FormulaToken( formula::svHybridCell ),
        mfDouble( f ), maString( rStr ),
        maFormula(std::move( aFormula )),
        mbEmptyDisplayedAsString( bEmptyDisplayedAsString)
{
    // caller, make up your mind...
    assert( !bEmptyDisplayedAsString || (f == 0.0 && rStr.getString().isEmpty()));
}
 
double ScHybridCellToken::GetDouble() const { return mfDouble; }
 
const svl::SharedString & ScHybridCellToken::GetString() const
{
    return maString;
}
 
bool ScHybridCellToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) &&
        mfDouble == r.GetDouble() && maString == r.GetString() &&
        maFormula == static_cast<const ScHybridCellToken &>(r).GetFormula();
}
 
bool ScTokenArray::AddFormulaToken(
    const css::sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, formula::ExternalReferenceHelper* pExtRef)
{
    bool bError = FormulaTokenArray::AddFormulaToken(rToken, rSPool, pExtRef);
    if ( bError )
    {
        bError = false;
        const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode);      // assuming equal values for the moment
 
        const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
        switch ( eClass )
        {
            case uno::TypeClass_STRUCT:
                {
                    uno::Type aType = rToken.Data.getValueType();
                    if ( aType.equals( cppu::UnoType<sheet::SingleReference>::get() ) )
                    {
                        ScSingleRefData aSingleRef;
                        sheet::SingleReference aApiRef;
                        rToken.Data >>= aApiRef;
                        lcl_SingleRefToCalc( aSingleRef, aApiRef );
                        if ( eOpCode == ocPush )
                            AddSingleReference( aSingleRef );
                        else if ( eOpCode == ocColRowName )
                            AddColRowName( aSingleRef );
                        else
                            bError = true;
                    }
                    else if ( aType.equals( cppu::UnoType<sheet::ComplexReference>::get() ) )
                    {
                        ScComplexRefData aComplRef;
                        sheet::ComplexReference aApiRef;
                        rToken.Data >>= aApiRef;
                        lcl_SingleRefToCalc( aComplRef.Ref1, aApiRef.Reference1 );
                        lcl_SingleRefToCalc( aComplRef.Ref2, aApiRef.Reference2 );
 
                        if ( eOpCode == ocPush )
                            AddDoubleReference( aComplRef );
                        else
                            bError = true;
                    }
                    else if ( aType.equals( cppu::UnoType<sheet::NameToken>::get() ) )
                    {
                        sheet::NameToken aTokenData;
                        rToken.Data >>= aTokenData;
                        if ( eOpCode == ocName )
                        {
                            SAL_WARN_IF( aTokenData.Sheet < -1 || std::numeric_limits<sal_Int16>::max() < aTokenData.Sheet,
                                    "sc.core",
                                    "ScTokenArray::AddFormulaToken - NameToken.Sheet out of limits: " << aTokenData.Sheet);
                            sal_Int16 nSheet = static_cast<sal_Int16>(aTokenData.Sheet);
                            AddRangeName(aTokenData.Index, nSheet);
                        }
                        else if (eOpCode == ocDBArea)
                            AddDBRange(aTokenData.Index);
                        else
                            bError = true;
                    }
                    else if ( aType.equals( cppu::UnoType<sheet::TableRefToken>::get() ) )
                    {
                        if (eOpCode == ocTableRef)
                        {
                            sheet::TableRefToken aTokenData;
                            rToken.Data >>= aTokenData;
                            ScTableRefToken* pToken = new ScTableRefToken( aTokenData.Index,
                                    static_cast<ScTableRefToken::Item>(aTokenData.Item));
                            if (Add(pToken))    // else pToken is deleted
                            {
                                if (aTokenData.Reference.Reference1 == aTokenData.Reference.Reference2)
                                {
                                    ScSingleRefData aRefData;
                                    lcl_SingleRefToCalc( aRefData, aTokenData.Reference.Reference1 );
                                    pToken->SetAreaRefRPN( new ScSingleRefToken( *mxSheetLimits, aRefData));
                                }
                                else
                                {
                                    ScComplexRefData aRefData;
                                    lcl_SingleRefToCalc( aRefData.Ref1, aTokenData.Reference.Reference1 );
                                    lcl_SingleRefToCalc( aRefData.Ref2, aTokenData.Reference.Reference2 );
                                    pToken->SetAreaRefRPN( new ScDoubleRefToken( *mxSheetLimits, aRefData));
                                }
                            }
                            else
                                bError = true;
                        }
                        else
                            bError = true;
                    }
                    else if ( aType.equals( cppu::UnoType<sheet::ExternalReference>::get() ) )
                    {
                        sheet::ExternalReference aApiExtRef;
                        if( (eOpCode == ocPush) && (rToken.Data >>= aApiExtRef) && (0 <= aApiExtRef.Index) && (aApiExtRef.Index <= SAL_MAX_UINT16) )
                        {
                            sal_uInt16 nFileId = static_cast< sal_uInt16 >( aApiExtRef.Index );
                            sheet::SingleReference aApiSRef;
                            sheet::ComplexReference aApiCRef;
                            OUString aName;
                            if( aApiExtRef.Reference >>= aApiSRef )
                            {
                                // try to resolve cache index to sheet name
                                size_t nCacheId = static_cast< size_t >( aApiSRef.Sheet );
                                OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
                                if( !aTabName.isEmpty() )
                                {
                                    ScSingleRefData aSingleRef;
                                    // convert column/row settings, set sheet index to absolute
                                    lcl_ExternalRefToCalc( aSingleRef, aApiSRef );
                                    AddExternalSingleReference( nFileId, rSPool.intern( aTabName), aSingleRef );
                                }
                                else
                                    bError = true;
                            }
                            else if( aApiExtRef.Reference >>= aApiCRef )
                            {
                                // try to resolve cache index to sheet name.
                                size_t nCacheId = static_cast< size_t >( aApiCRef.Reference1.Sheet );
                                OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
                                if( !aTabName.isEmpty() )
                                {
                                    ScComplexRefData aComplRef;
                                    // convert column/row settings, set sheet index to absolute
                                    lcl_ExternalRefToCalc( aComplRef.Ref1, aApiCRef.Reference1 );
                                    lcl_ExternalRefToCalc( aComplRef.Ref2, aApiCRef.Reference2 );
                                    // NOTE: This assumes that cached sheets are in consecutive order!
                                    aComplRef.Ref2.SetAbsTab(
                                        aComplRef.Ref1.Tab() + static_cast<SCTAB>(aApiCRef.Reference2.Sheet - aApiCRef.Reference1.Sheet));
                                    AddExternalDoubleReference( nFileId, rSPool.intern( aTabName), aComplRef );
                                }
                                else
                                    bError = true;
                            }
                            else if( aApiExtRef.Reference >>= aName )
                            {
                                if( !aName.isEmpty() )
                                    AddExternalName( nFileId, rSPool.intern( aName) );
                                else
                                    bError = true;
                            }
                            else
                                bError = true;
                        }
                        else
                            bError = true;
                    }
                    else
                        bError = true;      // unknown struct
                }
                break;
            case uno::TypeClass_SEQUENCE:
                {
                    if ( eOpCode != ocPush )
                        bError = true;      // not an inline array
                    else if (!rToken.Data.getValueType().equals( cppu::UnoType<
                                uno::Sequence< uno::Sequence< uno::Any >>>::get()))
                        bError = true;      // unexpected sequence type
                    else
                    {
                        ScMatrixRef xMat = ScSequenceToMatrix::CreateMixedMatrix( rToken.Data);
                        if (xMat)
                            AddMatrix( xMat);
                        else
                            bError = true;
                    }
                }
                break;
            default:
                bError = true;
        }
    }
    return bError;
}
 
void ScTokenArray::CheckForThreading( const FormulaToken& r )
{
#if HAVE_CPP_CONSTINIT_SORTED_VECTOR
    constinit
#endif
    static const o3tl::sorted_vector<OpCode> aThreadedCalcDenyList({
        ocIndirect,
        ocMacro,
        ocOffset,
        ocTableOp,
        ocCell,
        ocMatch,
        ocInfo,
        ocStyle,
        ocDBAverage,
        ocDBCount,
        ocDBCount2,
        ocDBGet,
        ocDBMax,
        ocDBMin,
        ocDBProduct,
        ocDBStdDev,
        ocDBStdDevP,
        ocDBSum,
        ocDBVar,
        ocDBVarP,
        ocText,
        ocSheet,
        ocExternal,
        ocDde,
        ocWebservice,
        ocGetPivotData
    });
 
    // Don't enable threading once we decided to disable it.
    if (!mbThreadingEnabled)
        return;
 
    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
    if (bThreadingProhibited)
    {
        mbThreadingEnabled = false;
        return;
    }
 
    OpCode eOp = r.GetOpCode();
 
    if (aThreadedCalcDenyList.find(eOp) != aThreadedCalcDenyList.end())
    {
        SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
            << "(" << int(eOp) << ") disables threaded calculation of formula group");
        mbThreadingEnabled = false;
        return;
    }
 
    if (eOp != ocPush)
        return;
 
    switch (r.GetType())
    {
        case svExternalDoubleRef:
        case svExternalSingleRef:
        case svExternalName:
        case svMatrix:
            SAL_INFO("sc.core.formulagroup", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType())
                << " disables threaded calculation of formula group");
            mbThreadingEnabled = false;
            return;
        default:
            break;
    }
}
 
void ScTokenArray::CheckToken( const FormulaToken& r )
{
    if (mbThreadingEnabled)
        CheckForThreading(r);
 
    if (IsFormulaVectorDisabled())
        return; // It's already disabled.  No more checking needed.
 
    OpCode eOp = r.GetOpCode();
 
    if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
    {
        if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
            ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
        {
            SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
                << "(" << int(eOp) << ") disables vectorisation for formula group");
            meVectorState = FormulaVectorDisabledNotInSubSet;
            mbOpenCLEnabled = false;
            return;
        }
 
        // We support vectorization for the following opcodes.
        switch (eOp)
        {
            case ocAverage:
            case ocMin:
            case ocMinA:
            case ocMax:
            case ocMaxA:
            case ocSum:
            case ocSumIfs:
            case ocSumProduct:
            case ocCount:
            case ocCount2:
            case ocVLookup:
            case ocXLookup:
            case ocXMatch:
            case ocFilter:
            case ocSort:
            case ocSortBy:
            case ocSLN:
            case ocIRR:
            case ocMIRR:
            case ocPMT:
            case ocRate:
            case ocRRI:
            case ocPpmt:
            case ocFisher:
            case ocFisherInv:
            case ocGamma:
            case ocGammaLn:
            case ocNotAvail:
            case ocGauss:
            case ocGeoMean:
            case ocHarMean:
            case ocSYD:
            case ocCorrel:
            case ocNegBinomVert:
            case ocPearson:
            case ocRSQ:
            case ocCos:
            case ocCosecant:
            case ocCosecantHyp:
            case ocISPMT:
            case ocPDuration:
            case ocSinHyp:
            case ocAbs:
            case ocPV:
            case ocSin:
            case ocTan:
            case ocTanHyp:
            case ocStandard:
            case ocWeibull:
            case ocMedian:
            case ocDDB:
            case ocFV:
            case ocVBD:
            case ocKurt:
            case ocNper:
            case ocNormDist:
            case ocArcCos:
            case ocSqrt:
            case ocArcCosHyp:
            case ocNPV:
            case ocStdNormDist:
            case ocNormInv:
            case ocSNormInv:
            case ocPermut:
            case ocPermutationA:
            case ocPhi:
            case ocIpmt:
            case ocConfidence:
            case ocIntercept:
            case ocDB:
            case ocLogInv:
            case ocArcCot:
            case ocCosHyp:
            case ocCritBinom:
            case ocArcCotHyp:
            case ocArcSin:
            case ocArcSinHyp:
            case ocArcTan:
            case ocArcTanHyp:
            case ocBitAnd:
            case ocForecast:
            case ocLogNormDist:
            case ocGammaDist:
            case ocLn:
            case ocRound:
            case ocCot:
            case ocCotHyp:
            case ocFDist:
            case ocVar:
            case ocChiDist:
            case ocPower:
            case ocOdd:
            case ocChiSqDist:
            case ocChiSqInv:
            case ocGammaInv:
            case ocFloor:
            case ocFInv:
            case ocFTest:
            case ocB:
            case ocBetaDist:
            case ocExp:
            case ocLog10:
            case ocExpDist:
            case ocAverageIfs:
            case ocCountIfs:
            case ocCombinA:
            case ocEven:
            case ocLog:
            case ocMod:
            case ocTrunc:
            case ocSkew:
            case ocArcTan2:
            case ocBitOr:
            case ocBitLshift:
            case ocBitRshift:
            case ocBitXor:
            case ocChiInv:
            case ocPoissonDist:
            case ocSumSQ:
            case ocSkewp:
            case ocBinomDist:
            case ocVarP:
            case ocCeil:
            case ocCombin:
            case ocDevSq:
            case ocStDev:
            case ocSlope:
            case ocSTEYX:
            case ocZTest:
            case ocPi:
            case ocRandom:
            case ocProduct:
            case ocHypGeomDist:
            case ocSumX2MY2:
            case ocSumX2DY2:
            case ocBetaInv:
            case ocTTest:
            case ocTDist:
            case ocTInv:
            case ocSumXMY2:
            case ocStDevP:
            case ocCovar:
            case ocAnd:
            case ocOr:
            case ocNot:
            case ocXor:
            case ocDBMax:
            case ocDBMin:
            case ocDBProduct:
            case ocDBAverage:
            case ocDBStdDev:
            case ocDBStdDevP:
            case ocDBSum:
            case ocDBVar:
            case ocDBVarP:
            case ocAverageIf:
            case ocDBCount:
            case ocDBCount2:
            case ocDeg:
            case ocRoundUp:
            case ocRoundDown:
            case ocInt:
            case ocRad:
            case ocCountIf:
            case ocIsEven:
            case ocIsOdd:
            case ocFact:
            case ocAverageA:
            case ocVarA:
            case ocVarPA:
            case ocStDevA:
            case ocStDevPA:
            case ocSecant:
            case ocSecantHyp:
            case ocSumIf:
            case ocNegSub:
            case ocAveDev:
            case ocMatSequence:
            case ocRandArray:
            case ocUnique:
            // Don't change the state.
            break;
            default:
                SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
                    << "(" << int(eOp) << ") disables vectorisation for formula group");
                meVectorState = FormulaVectorDisabledByOpCode;
                mbOpenCLEnabled = false;
                return;
        }
    }
    else if (eOp == ocPush)
    {
        // This is a stack variable.  See if this is a reference.
 
        switch (r.GetType())
        {
            case svByte:
            case svDouble:
            case svString:
                // Don't change the state.
            break;
            case svSingleRef:
            case svDoubleRef:
                // Depends on the reference state.
                meVectorState = FormulaVectorCheckReference;
            break;
            case svError:
            case svEmptyCell:
            case svExternal:
            case svExternalDoubleRef:
            case svExternalName:
            case svExternalSingleRef:
            case svFAP:
            case svHybridCell:
            case svIndex:
            case svJump:
            case svJumpMatrix:
            case svMatrix:
            case svMatrixCell:
            case svMissing:
            case svRefList:
            case svSep:
            case svUnknown:
                // We don't support vectorization on these.
                SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
                meVectorState = FormulaVectorDisabledByStackVariable;
                mbOpenCLEnabled = false;
                return;
            default:
                ;
        }
    }
    else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
    {
        if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
            ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
        {
            SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
                << "(" << int(eOp) << ") disables vectorisation for formula group");
            meVectorState = FormulaVectorDisabledNotInSubSet;
            mbOpenCLEnabled = false;
            return;
        }
    }
    else
    {
        // All the rest, special commands, separators, error codes, ...
        switch (eOp)
        {
            default:
                // Default is off, no vectorization.
                // Mentioning some specific values below to indicate why.
 
            case ocName:
                // Named expression would need "recursive" handling of its
                // token array for vector state in
                // ScFormulaCell::InterpretFormulaGroup() and below.
 
            case ocDBArea:
                // Certainly not a vectorization of the entire area...
 
            case ocTableRef:
                // May result in a single cell or range reference, depending on
                // context.
 
            case ocColRowName:
                // The associated reference is the name cell with which to
                // create the implicit intersection.
 
            case ocColRowNameAuto:
                // Auto column/row names lead to references computed in
                // interpreter.
 
                SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
                    << "(" << int(eOp) << ") disables vectorisation for formula group");
                meVectorState = FormulaVectorDisabledByOpCode;
                mbOpenCLEnabled = false;
                return;
 
            // Known good, don't change state.
            case ocStop:
            case ocExternal:
            case ocOpen:
            case ocClose:
            case ocSep:
            case ocArrayOpen:
            case ocArrayRowSep:
            case ocArrayColSep:
            case ocArrayClose:
            case ocMissing:
            case ocBad:
            case ocSpaces:
            case ocWhitespace:
            case ocSkip:
            case ocPercentSign:
            case ocErrNull:
            case ocErrDivZero:
            case ocErrValue:
            case ocErrRef:
            case ocErrName:
            case ocErrNum:
            case ocErrNA:
            break;
            case ocIf:
            case ocIfError:
            case ocIfNA:
            case ocChoose:
            case ocLet:
                // Jump commands are now supported.
            break;
        }
    }
}
 
bool ScTokenArray::ImplGetReference( ScRange& rRange, const ScAddress& rPos, bool bValidOnly ) const
{
    bool bIs = false;
    if ( pCode && nLen == 1 )
    {
        const FormulaToken* pToken = pCode[0];
        if ( pToken )
        {
            if ( pToken->GetType() == svSingleRef )
            {
                const ScSingleRefData& rRef = *static_cast<const ScSingleRefToken*>(pToken)->GetSingleRef();
                rRange.aStart = rRange.aEnd = rRef.toAbs(*mxSheetLimits, rPos);
                bIs = !bValidOnly || mxSheetLimits->ValidAddress(rRange.aStart);
            }
            else if ( pToken->GetType() == svDoubleRef )
            {
                const ScComplexRefData& rCompl = *static_cast<const ScDoubleRefToken*>(pToken)->GetDoubleRef();
                const ScSingleRefData& rRef1 = rCompl.Ref1;
                const ScSingleRefData& rRef2 = rCompl.Ref2;
                rRange.aStart = rRef1.toAbs(*mxSheetLimits, rPos);
                rRange.aEnd   = rRef2.toAbs(*mxSheetLimits, rPos);
                bIs = !bValidOnly || mxSheetLimits->ValidRange(rRange);
            }
        }
    }
    return bIs;
}
 
namespace {
 
// we want to compare for similar not identical formulae
// so we can't use actual row & column indices.
size_t HashSingleRef( const ScSingleRefData& rRef )
{
    size_t nVal = 0;
 
    nVal += size_t(rRef.IsColRel());
    nVal += (size_t(rRef.IsRowRel()) << 1);
    nVal += (size_t(rRef.IsTabRel()) << 2);
 
    return nVal;
}
 
}
 
void ScTokenArray::GenHash()
{
    static const OUStringHash aHasher;
 
    size_t nHash = 1;
    OpCode eOp;
    StackVar eType;
    const formula::FormulaToken* p;
    sal_uInt16 n = std::min<sal_uInt16>(nLen, 20);
    for (sal_uInt16 i = 0; i < n; ++i)
    {
        p = pCode[i];
        eOp = p->GetOpCode();
        if (eOp == ocPush)
        {
            // This is stack variable. Do additional differentiation.
            eType = p->GetType();
            switch (eType)
            {
                case svByte:
                {
                    // Constant value.
                    sal_uInt8 nVal = p->GetByte();
                    nHash += static_cast<size_t>(nVal);
                }
                break;
                case svDouble:
                {
                    // Constant value.
                    double fVal = p->GetDouble();
                    nHash += std::hash<double>()(fVal);
                }
                break;
                case svString:
                {
                    // Constant string.
                    OUString aStr = p->GetString().getString();
                    nHash += aHasher(aStr);
                }
                break;
                case svSingleRef:
                {
                    size_t nVal = HashSingleRef(*p->GetSingleRef());
                    nHash += nVal;
                }
                break;
                case svDoubleRef:
                {
                    const ScComplexRefData& rRef = *p->GetDoubleRef();
                    size_t nVal1 = HashSingleRef(rRef.Ref1);
                    size_t nVal2 = HashSingleRef(rRef.Ref2);
                    nHash += nVal1;
                    nHash += nVal2;
                }
                break;
                default:
                    // Use the opcode value in all the other cases.
                    nHash += static_cast<size_t>(eOp);
            }
        }
        else
            // Use the opcode value in all the other cases.
            nHash += static_cast<size_t>(eOp);
 
        nHash = (nHash << 4) - nHash;
    }
 
    mnHashValue = nHash;
}
 
void ScTokenArray::ResetVectorState()
{
    mbOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
    meVectorState = mbOpenCLEnabled ? FormulaVectorEnabled : FormulaVectorDisabled;
    mbThreadingEnabled = ScCalcConfig::isThreadingEnabled();
}
 
bool ScTokenArray::IsFormulaVectorDisabled() const
{
    switch (meVectorState)
    {
        case FormulaVectorDisabled:
        case FormulaVectorDisabledByOpCode:
        case FormulaVectorDisabledByStackVariable:
        case FormulaVectorDisabledNotInSubSet:
            return true;
        default:
            ;
    }
 
    return false;
}
 
bool ScTokenArray::IsInvariant() const
{
    FormulaToken** p = pCode.get();
    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
    for (; p != pEnd; ++p)
    {
        switch ((*p)->GetType())
        {
            case svSingleRef:
            case svExternalSingleRef:
            {
                const ScSingleRefData& rRef = *(*p)->GetSingleRef();
                if (rRef.IsRowRel())
                    return false;
            }
            break;
            case svDoubleRef:
            case svExternalDoubleRef:
            {
                const ScComplexRefData& rRef = *(*p)->GetDoubleRef();
                if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
                    return false;
            }
            break;
            case svIndex:
                return false;
            default:
                ;
        }
    }
 
    return true;
}
 
bool ScTokenArray::IsReference( ScRange& rRange, const ScAddress& rPos ) const
{
    return ImplGetReference(rRange, rPos, false);
}
 
bool ScTokenArray::IsValidReference( ScRange& rRange, const ScAddress& rPos ) const
{
    return ImplGetReference(rRange, rPos, true);
}
 
ScTokenArray::ScTokenArray(const ScDocument& rDoc) :
    mxSheetLimits(&rDoc.GetSheetLimits()),
    mnHashValue(0)
{
    ResetVectorState();
}
 
ScTokenArray::ScTokenArray(ScSheetLimits& rLimits) :
    mxSheetLimits(&rLimits),
    mnHashValue(0)
{
    ResetVectorState();
}
 
ScTokenArray::~ScTokenArray()
{
}
 
ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr )
{
    Clear();
    Assign( rArr );
    mnHashValue = rArr.mnHashValue;
    meVectorState = rArr.meVectorState;
    mbOpenCLEnabled = rArr.mbOpenCLEnabled;
    mbThreadingEnabled = rArr.mbThreadingEnabled;
    return *this;
}
 
ScTokenArray& ScTokenArray::operator=( ScTokenArray&& rArr )
{
    mxSheetLimits = std::move(rArr.mxSheetLimits);
    mnHashValue = rArr.mnHashValue;
    meVectorState = rArr.meVectorState;
    mbOpenCLEnabled = rArr.mbOpenCLEnabled;
    mbThreadingEnabled = rArr.mbThreadingEnabled;
    Move(std::move(rArr));
    return *this;
}
 
bool ScTokenArray::EqualTokens( const ScTokenArray* pArr2) const
{
    // We only compare the non-RPN array
    if ( pArr2->nLen != nLen )
        return false;
 
    FormulaToken** ppToken1 = GetArray();
    FormulaToken** ppToken2 = pArr2->GetArray();
    for (sal_uInt16 i=0; i<nLen; i++)
    {
        if ( ppToken1[i] != ppToken2[i] &&
             !(*ppToken1[i] == *ppToken2[i]) )
            return false; // Difference
    }
    return true; // All entries are the same
}
 
void ScTokenArray::Clear()
{
    mnHashValue = 0;
    ResetVectorState();
    FormulaTokenArray::Clear();
}
 
std::unique_ptr<ScTokenArray> ScTokenArray::Clone() const
{
    std::unique_ptr<ScTokenArray> p(new ScTokenArray(*mxSheetLimits));
    p->nLen = nLen;
    p->nRPN = nRPN;
    p->nMode = nMode;
    p->nError = nError;
    p->bHyperLink = bHyperLink;
    p->mnHashValue = mnHashValue;
    p->meVectorState = meVectorState;
    p->mbOpenCLEnabled = mbOpenCLEnabled;
    p->mbThreadingEnabled = mbThreadingEnabled;
    p->mbFromRangeName = mbFromRangeName;
    p->mbShareable = mbShareable;
 
    FormulaToken** pp;
    if( nLen )
    {
        p->pCode.reset(new FormulaToken*[ nLen ]);
        pp = p->pCode.get();
        memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
        {
            *pp = (*pp)->Clone();
            (*pp)->IncRef();
        }
    }
    if( nRPN )
    {
        pp = p->pRPN = new FormulaToken*[ nRPN ];
        memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
        {
            FormulaToken* t = *pp;
            if( t->GetRef() > 1 )
            {
                FormulaToken** p2 = pCode.get();
                sal_uInt16 nIdx = 0xFFFF;
                for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
                {
                    if( *p2 == t )
                    {
                        nIdx = j; break;
                    }
                }
                if( nIdx == 0xFFFF )
                    *pp = t->Clone();
                else
                    *pp = p->pCode[ nIdx ];
            }
            else
                *pp = t->Clone();
            (*pp)->IncRef();
        }
    }
    return p;
}
 
ScTokenArray ScTokenArray::CloneValue() const
{
    ScTokenArray aNew(*mxSheetLimits);
    aNew.nLen = nLen;
    aNew.nRPN = nRPN;
    aNew.nMode = nMode;
    aNew.nError = nError;
    aNew.bHyperLink = bHyperLink;
    aNew.mnHashValue = mnHashValue;
    aNew.meVectorState = meVectorState;
    aNew.mbOpenCLEnabled = mbOpenCLEnabled;
    aNew.mbThreadingEnabled = mbThreadingEnabled;
    aNew.mbFromRangeName = mbFromRangeName;
    aNew.mbShareable = mbShareable;
 
    FormulaToken** pp;
    if( nLen )
    {
        aNew.pCode.reset(new FormulaToken*[ nLen ]);
        pp = aNew.pCode.get();
        memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
        {
            *pp = (*pp)->Clone();
            (*pp)->IncRef();
        }
    }
    if( nRPN )
    {
        pp = aNew.pRPN = new FormulaToken*[ nRPN ];
        memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
        {
            FormulaToken* t = *pp;
            if( t->GetRef() > 1 )
            {
                FormulaToken** p2 = pCode.get();
                sal_uInt16 nIdx = 0xFFFF;
                for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
                {
                    if( *p2 == t )
                    {
                        nIdx = j; break;
                    }
                }
                if( nIdx == 0xFFFF )
                    *pp = t->Clone();
                else
                    *pp = aNew.pCode[ nIdx ];
            }
            else
                *pp = t->Clone();
            (*pp)->IncRef();
        }
    }
    return aNew;
}
 
FormulaToken* ScTokenArray::AddRawToken( const ScRawToken& r )
{
    return Add( r.CreateToken(*mxSheetLimits) );
}
 
// Utility function to ensure that there is strict alternation of values and
// separators.
static bool
checkArraySep( bool & bPrevWasSep, bool bNewVal )
{
    bool bResult = (bPrevWasSep == bNewVal);
    bPrevWasSep = bNewVal;
    return bResult;
}
 
FormulaToken* ScTokenArray::MergeArray( )
{
    int nCol = -1, nRow = 0;
    int i, nPrevRowSep = -1, nStart = 0;
    bool bPrevWasSep = false; // top of stack is ocArrayClose
    FormulaToken* t;
    bool bNumeric = false;  // numeric value encountered in current element
 
    // (1) Iterate from the end to the start to find matrix dims
    // and do basic validation.
    for ( i = nLen ; i-- > nStart ; )
    {
        t = pCode[i];
        switch ( t->GetOpCode() )
        {
            case ocPush :
                if( checkArraySep( bPrevWasSep, false ) )
                {
                    return nullptr;
                }
 
                // no references or nested arrays
                if ( t->GetType() != svDouble  && t->GetType() != svString )
                {
                    return nullptr;
                }
                bNumeric = (t->GetType() == svDouble);
            break;
 
            case ocMissing :
            case ocTrue :
            case ocFalse :
                if( checkArraySep( bPrevWasSep, false ) )
                {
                    return nullptr;
                }
                bNumeric = false;
            break;
 
            case ocArrayColSep :
            case ocSep :
                if( checkArraySep( bPrevWasSep, true ) )
                {
                    return nullptr;
                }
                bNumeric = false;
            break;
 
            case ocArrayClose :
                // not possible with the , but check just in case
                // something changes in the future
                if( i != (nLen-1))
                {
                    return nullptr;
                }
 
                if( checkArraySep( bPrevWasSep, true ) )
                {
                    return nullptr;
                }
 
                nPrevRowSep = i;
                bNumeric = false;
            break;
 
            case ocArrayOpen :
                nStart = i; // stop iteration
                [[fallthrough]]; // to ArrayRowSep
 
            case ocArrayRowSep :
                if( checkArraySep( bPrevWasSep, true ) )
                {
                    return nullptr;
                }
 
                if( nPrevRowSep < 0 ||              // missing ocArrayClose
                    ((nPrevRowSep - i) % 2) == 1)   // no complex elements
                {
                    return nullptr;
                }
 
                if( nCol < 0 )
                {
                    nCol = (nPrevRowSep - i) / 2;
                }
                else if( (nPrevRowSep - i)/2 != nCol)   // irregular array
                {
                    return nullptr;
                }
 
                nPrevRowSep = i;
                nRow++;
                bNumeric = false;
            break;
 
            case ocNegSub :
            case ocAdd :
                // negation or unary plus must precede numeric value
                if( !bNumeric )
                {
                    return nullptr;
                }
                --nPrevRowSep;      // shorten this row by 1
                bNumeric = false;   // one level only, no --42
            break;
 
            case ocSpaces :
            case ocWhitespace :
                // ignore spaces
                --nPrevRowSep;      // shorten this row by 1
            break;
 
            default :
                // no functions or operators
                return nullptr;
        }
    }
    if( nCol <= 0 || nRow <= 0 )
        return nullptr;
 
    int nSign = 1;
    ScMatrix* pArray = new ScMatrix(nCol, nRow, 0.0);
    for ( i = nStart, nCol = 0, nRow = 0 ; i < nLen ; i++ )
    {
        t = pCode[i];
 
        switch ( t->GetOpCode() )
        {
            case ocPush :
                if ( t->GetType() == svDouble )
                {
                    pArray->PutDouble( t->GetDouble() * nSign, nCol, nRow );
                    nSign = 1;
                }
                else if ( t->GetType() == svString )
                {
                    pArray->PutString(t->GetString(), nCol, nRow);
                }
            break;
 
            case ocMissing :
                pArray->PutEmpty( nCol, nRow );
            break;
 
            case ocTrue :
                pArray->PutBoolean( true, nCol, nRow );
            break;
 
            case ocFalse :
                pArray->PutBoolean( false, nCol, nRow );
            break;
 
            case ocArrayColSep :
            case ocSep :
                nCol++;
            break;
 
            case ocArrayRowSep :
                nRow++; nCol = 0;
            break;
 
            case ocNegSub :
                nSign = -nSign;
            break;
 
            default :
                break;
        }
        pCode[i] = nullptr;
        t->DecRef();
    }
    nLen = sal_uInt16( nStart );
    return AddMatrix( pArray );
}
 
void ScTokenArray::MergeRangeReference( const ScAddress & rPos )
{
    if (!pCode || !nLen)
        return;
    sal_uInt16 nIdx = nLen;
 
    // The actual types are checked in extendRangeReference().
    FormulaToken *p3 = PeekPrev(nIdx); // ref
    if (!p3)
        return;
    FormulaToken *p2 = PeekPrev(nIdx); // ocRange
    if (!p2 || p2->GetOpCode() != ocRange)
        return;
    FormulaToken *p1 = PeekPrev(nIdx); // ref
    if (!p1)
        return;
    FormulaTokenRef p = extendRangeReference( *mxSheetLimits, *p1, *p3, rPos, true);
    if (p)
    {
        p->IncRef();
        p1->DecRef();
        p2->DecRef();
        p3->DecRef();
        nLen -= 2;
        pCode[ nLen-1 ] = p.get();
    }
}
 
FormulaToken* ScTokenArray::AddOpCode( OpCode e )
{
    ScRawToken t;
    t.SetOpCode( e );
    return AddRawToken( t );
}
 
FormulaToken* ScTokenArray::AddSingleReference( const ScSingleRefData& rRef )
{
    return Add( new ScSingleRefToken( *mxSheetLimits, rRef ) );
}
 
FormulaToken* ScTokenArray::AddMatrixSingleReference( const ScSingleRefData& rRef )
{
    return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocMatRef ) );
}
 
FormulaToken* ScTokenArray::AddDoubleReference( const ScComplexRefData& rRef )
{
    return Add( new ScDoubleRefToken(*mxSheetLimits, rRef ) );
}
 
FormulaToken* ScTokenArray::AddMatrix( const ScMatrixRef& p )
{
    return Add( new ScMatrixToken( p ) );
}
 
void ScTokenArray::AddRangeName( sal_uInt16 n, sal_Int16 nSheet )
{
    Add( new FormulaIndexToken( ocName, n, nSheet));
}
 
FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n )
{
    return Add( new FormulaIndexToken( ocDBArea, n));
}
 
FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName )
{
    return Add( new ScExternalNameToken(nFileId, rName) );
}
 
void ScTokenArray::AddExternalSingleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
        const ScSingleRefData& rRef )
{
    Add( new ScExternalSingleRefToken(nFileId, rTabName, rRef) );
}
 
FormulaToken* ScTokenArray::AddExternalDoubleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
        const ScComplexRefData& rRef )
{
    return Add( new ScExternalDoubleRefToken(nFileId, rTabName, rRef) );
}
 
FormulaToken* ScTokenArray::AddColRowName( const ScSingleRefData& rRef )
{
    return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocColRowName ) );
}
 
void ScTokenArray::AssignXMLString( const OUString &rText, const OUString &rFormulaNmsp )
{
    sal_uInt16 nTokens = 1;
    FormulaToken *aTokens[2];
 
    aTokens[0] = new FormulaStringOpToken( ocStringXML, svl::SharedString( rText) );    // string not interned
    if( !rFormulaNmsp.isEmpty() )
        aTokens[ nTokens++ ] = new FormulaStringOpToken( ocStringXML,
                svl::SharedString( rFormulaNmsp) );   // string not interned
 
    Assign( nTokens, aTokens );
}
 
bool ScTokenArray::GetAdjacentExtendOfOuterFuncRefs( SCCOLROW& nExtend,
        const ScAddress& rPos, ScDirection eDir )
{
    SCCOL nCol = 0;
    SCROW nRow = 0;
    switch ( eDir )
    {
        case DIR_BOTTOM :
            if ( rPos.Row() >= mxSheetLimits->mnMaxRow )
                return false;
            nExtend = rPos.Row();
            nRow = nExtend + 1;
        break;
        case DIR_RIGHT :
            if ( rPos.Col() >= mxSheetLimits->mnMaxCol )
                return false;
            nExtend = rPos.Col();
            nCol = static_cast<SCCOL>(nExtend) + 1;
        break;
        case DIR_TOP :
            if ( rPos.Row() <= 0 )
                return false;
            nExtend = rPos.Row();
            nRow = nExtend - 1;
        break;
        case DIR_LEFT :
            if ( rPos.Col() <= 0 )
                return false;
            nExtend = rPos.Col();
            nCol = static_cast<SCCOL>(nExtend) - 1;
        break;
        default:
            OSL_FAIL( "unknown Direction" );
            return false;
    }
    if ( pRPN && nRPN )
    {
        FormulaToken* t = pRPN[nRPN-1];
        if ( t->GetType() == svByte )
        {
            sal_uInt8 nParamCount = t->GetByte();
            if ( nParamCount && nRPN > nParamCount )
            {
                bool bRet = false;
                sal_uInt16 nParam = nRPN - nParamCount - 1;
                for ( ; nParam < nRPN-1; nParam++ )
                {
                    FormulaToken* p = pRPN[nParam];
                    switch ( p->GetType() )
                    {
                        case svSingleRef :
                        {
                            ScSingleRefData& rRef = *p->GetSingleRef();
                            ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
                            switch ( eDir )
                            {
                                case DIR_BOTTOM :
                                    if (aAbs.Row() == nRow && aAbs.Row() > nExtend)
                                    {
                                        nExtend = aAbs.Row();
                                        bRet = true;
                                    }
                                break;
                                case DIR_RIGHT :
                                    if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) > nExtend)
                                    {
                                        nExtend = aAbs.Col();
                                        bRet = true;
                                    }
                                break;
                                case DIR_TOP :
                                    if (aAbs.Row() == nRow && aAbs.Row() < nExtend)
                                    {
                                        nExtend = aAbs.Row();
                                        bRet = true;
                                    }
                                break;
                                case DIR_LEFT :
                                    if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) < nExtend)
                                    {
                                        nExtend = aAbs.Col();
                                        bRet = true;
                                    }
                                break;
                            }
                        }
                        break;
                        case svDoubleRef :
                        {
                            ScComplexRefData& rRef = *p->GetDoubleRef();
                            ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
                            switch ( eDir )
                            {
                                case DIR_BOTTOM :
                                    if (aAbs.aStart.Row() == nRow && aAbs.aEnd.Row() > nExtend)
                                    {
                                        nExtend = aAbs.aEnd.Row();
                                        bRet = true;
                                    }
                                break;
                                case DIR_RIGHT :
                                    if (aAbs.aStart.Col() == nCol && static_cast<SCCOLROW>(aAbs.aEnd.Col()) > nExtend)
                                    {
                                        nExtend = aAbs.aEnd.Col();
                                        bRet = true;
                                    }
                                break;
                                case DIR_TOP :
                                    if (aAbs.aEnd.Row() == nRow && aAbs.aStart.Row() < nExtend)
                                    {
                                        nExtend = aAbs.aStart.Row();
                                        bRet = true;
                                    }
                                break;
                                case DIR_LEFT :
                                    if (aAbs.aEnd.Col() == nCol && static_cast<SCCOLROW>(aAbs.aStart.Col()) < nExtend)
                                    {
                                        nExtend = aAbs.aStart.Col();
                                        bRet = true;
                                    }
                                break;
                            }
                        }
                        break;
                        default:
                        {
                            // added to avoid warnings
                        }
                    } // switch
                } // for
                return bRet;
            }
        }
    }
    return false;
}
 
namespace {
 
void GetExternalTableData(const ScDocument* pOldDoc, const ScDocument* pNewDoc, const SCTAB nTab, OUString& rTabName, sal_uInt16& rFileId)
{
    const OUString& aFileName = pOldDoc->GetFileURL();
    rFileId = pNewDoc->GetExternalRefManager()->getExternalFileId(aFileName);
    rTabName = pOldDoc->GetCopyTabName(nTab);
    if (rTabName.isEmpty())
        pOldDoc->GetName(nTab, rTabName);
}
 
bool IsInCopyRange( const ScRange& rRange, const ScDocument* pClipDoc )
{
    ScClipParam& rClipParam = const_cast<ScDocument*>(pClipDoc)->GetClipParam();
    return rClipParam.maRanges.Contains(rRange);
}
 
bool SkipReference(formula::FormulaToken* pToken, const ScAddress& rPos, const ScDocument& rOldDoc, bool bRangeName, bool bCheckCopyArea)
{
    ScRange aRange;
 
    if (!ScRefTokenHelper::getRangeFromToken(&rOldDoc, aRange, pToken, rPos))
        return true;
 
    if (bRangeName && aRange.aStart.Tab() == rPos.Tab())
    {
        switch (pToken->GetType())
        {
            case svDoubleRef:
                {
                    ScSingleRefData& rRef = *pToken->GetSingleRef2();
                    if (rRef.IsColRel() || rRef.IsRowRel())
                        return true;
                }
                [[fallthrough]];
            case svSingleRef:
                {
                    ScSingleRefData& rRef = *pToken->GetSingleRef();
                    if (rRef.IsColRel() || rRef.IsRowRel())
                        return true;
                }
                break;
            default:
                break;
        }
    }
 
    if (bCheckCopyArea && IsInCopyRange(aRange, &rOldDoc))
        return true;
 
    return false;
}
 
void AdjustSingleRefData( ScSingleRefData& rRef, const ScAddress& rOldPos, const ScAddress& rNewPos)
{
    SCCOL nCols = rNewPos.Col() - rOldPos.Col();
    SCROW nRows = rNewPos.Row() - rOldPos.Row();
    SCTAB nTabs = rNewPos.Tab() - rOldPos.Tab();
 
    if (!rRef.IsColRel())
        rRef.IncCol(nCols);
 
    if (!rRef.IsRowRel())
        rRef.IncRow(nRows);
 
    if (!rRef.IsTabRel())
        rRef.IncTab(nTabs);
}
 
}
 
void ScTokenArray::ReadjustAbsolute3DReferences( const ScDocument& rOldDoc, ScDocument& rNewDoc, const ScAddress& rPos, bool bRangeName )
{
    for ( sal_uInt16 j=0; j<nLen; ++j )
    {
        switch ( pCode[j]->GetType() )
        {
            case svDoubleRef :
            {
                if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
                    continue;
 
                ScComplexRefData& rRef = *pCode[j]->GetDoubleRef();
                ScSingleRefData& rRef2 = rRef.Ref2;
                ScSingleRefData& rRef1 = rRef.Ref1;
 
                if ( (rRef2.IsFlag3D() && !rRef2.IsTabRel()) || (rRef1.IsFlag3D() && !rRef1.IsTabRel()) )
                {
                    OUString aTabName;
                    sal_uInt16 nFileId;
                    GetExternalTableData(&rOldDoc, &rNewDoc, rRef1.Tab(), aTabName, nFileId);
                    ReplaceToken( j, new ScExternalDoubleRefToken( nFileId,
                                rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
                    // ATTENTION: rRef can't be used after this point
                }
            }
            break;
            case svSingleRef :
            {
                if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
                    continue;
 
                ScSingleRefData& rRef = *pCode[j]->GetSingleRef();
 
                if ( rRef.IsFlag3D() && !rRef.IsTabRel() )
                {
                    OUString aTabName;
                    sal_uInt16 nFileId;
                    GetExternalTableData(&rOldDoc, &rNewDoc, rRef.Tab(), aTabName, nFileId);
                    ReplaceToken( j, new ScExternalSingleRefToken( nFileId,
                                rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
                    // ATTENTION: rRef can't be used after this point
                }
            }
            break;
            default:
            {
                // added to avoid warnings
            }
        }
    }
}
 
void ScTokenArray::AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddress& rOldPos, const ScAddress& rNewPos,
        bool bCheckCopyRange)
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, true);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch ( p->GetType() )
            {
                case svDoubleRef :
                    {
                        if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
                            continue;
 
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScSingleRefData& rRef2 = rRef.Ref2;
                        ScSingleRefData& rRef1 = rRef.Ref1;
 
                        AdjustSingleRefData( rRef1, rOldPos, rNewPos );
                        AdjustSingleRefData( rRef2, rOldPos, rNewPos );
                    }
                    break;
                case svSingleRef :
                    {
                        if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
                            continue;
 
                        ScSingleRefData& rRef = *p->GetSingleRef();
 
                        AdjustSingleRefData( rRef, rOldPos, rNewPos );
                    }
                    break;
                default:
                    {
                        // added to avoid warnings
                    }
            }
        }
    }
}
 
void ScTokenArray::AdjustSheetLocalNameReferences( SCTAB nOldTab, SCTAB nNewTab )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch ( p->GetType() )
            {
                case svDoubleRef :
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScSingleRefData& rRef2 = rRef.Ref2;
                        ScSingleRefData& rRef1 = rRef.Ref1;
 
                        if (!rRef1.IsTabRel() && rRef1.Tab() == nOldTab)
                            rRef1.SetAbsTab( nNewTab);
                        if (!rRef2.IsTabRel() && rRef2.Tab() == nOldTab)
                            rRef2.SetAbsTab( nNewTab);
                        if (!rRef1.IsTabRel() && !rRef2.IsTabRel() && rRef1.Tab() > rRef2.Tab())
                        {
                            SCTAB nTab = rRef1.Tab();
                            rRef1.SetAbsTab( rRef2.Tab());
                            rRef2.SetAbsTab( nTab);
                        }
                    }
                    break;
                case svSingleRef :
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
 
                        if (!rRef.IsTabRel() && rRef.Tab() == nOldTab)
                            rRef.SetAbsTab( nNewTab);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
bool ScTokenArray::ReferencesSheet( SCTAB nTab, SCTAB nPosTab ) const
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken* const * const pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            const FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch ( p->GetType() )
            {
                case svDoubleRef :
                    {
                        const ScComplexRefData& rRef = *p->GetDoubleRef();
                        const ScSingleRefData& rRef2 = rRef.Ref2;
                        const ScSingleRefData& rRef1 = rRef.Ref1;
 
                        SCTAB nTab1 = (rRef1.IsTabRel() ? rRef1.Tab() + nPosTab : rRef1.Tab());
                        SCTAB nTab2 = (rRef2.IsTabRel() ? rRef2.Tab() + nPosTab : rRef2.Tab());
                        if (nTab1 <= nTab && nTab <= nTab2)
                            return true;
                    }
                    break;
                case svSingleRef :
                    {
                        const ScSingleRefData& rRef = *p->GetSingleRef();
                        if (rRef.IsTabRel())
                        {
                            if (rRef.Tab() + nPosTab == nTab)
                                return true;
                        }
                        else
                        {
                            if (rRef.Tab() == nTab)
                                return true;
                        }
                    }
                    break;
                default:
                    ;
            }
        }
    }
    return false;
}
 
namespace {
 
ScRange getSelectedRange( const sc::RefUpdateContext& rCxt )
{
    ScRange aSelectedRange(ScAddress::INITIALIZE_INVALID);
    if (rCxt.mnColDelta < 0)
    {
        // Delete and shift to left.
        aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta, rCxt.maRange.aStart.Row(), rCxt.maRange.aStart.Tab());
        aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
    }
    else if (rCxt.mnRowDelta < 0)
    {
        // Delete and shift up.
        aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta, rCxt.maRange.aStart.Tab());
        aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()-1, rCxt.maRange.aEnd.Tab());
    }
    else if (rCxt.mnTabDelta < 0)
    {
        // Deleting sheets.
        // TODO : Figure out what to do here.
    }
    else if (rCxt.mnColDelta > 0)
    {
        // Insert and shift to the right.
        aSelectedRange.aStart = rCxt.maRange.aStart;
        aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
    }
    else if (rCxt.mnRowDelta > 0)
    {
        // Insert and shift down.
        aSelectedRange.aStart = rCxt.maRange.aStart;
        aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta-1, rCxt.maRange.aEnd.Tab());
    }
    else if (rCxt.mnTabDelta > 0)
    {
        // Inserting sheets.
        // TODO : Figure out what to do here.
    }
 
    return aSelectedRange;
}
 
void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    if (rCxt.mnColDelta < 0)
        rRef.SetColDeleted(true);
    else if (rCxt.mnRowDelta < 0)
        rRef.SetRowDeleted(true);
    else if (rCxt.mnTabDelta < 0)
        rRef.SetTabDeleted(true);
}
 
void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    if (rCxt.mnColDelta)
    {
        if (rRef.IsColDeleted())
            rRef.SetColDeleted(false);
    }
    else if (rCxt.mnRowDelta)
    {
        if (rRef.IsRowDeleted())
            rRef.SetRowDeleted(false);
    }
    else if (rCxt.mnTabDelta)
    {
        if (rRef.IsTabDeleted())
            rRef.SetTabDeleted(false);
    }
}
 
void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    if (rCxt.mnColDelta < 0)
    {
        rRef.Ref1.SetColDeleted(true);
        rRef.Ref2.SetColDeleted(true);
    }
    else if (rCxt.mnRowDelta < 0)
    {
        rRef.Ref1.SetRowDeleted(true);
        rRef.Ref2.SetRowDeleted(true);
    }
    else if (rCxt.mnTabDelta < 0)
    {
        rRef.Ref1.SetTabDeleted(true);
        rRef.Ref2.SetTabDeleted(true);
    }
}
 
void restoreDeletedRef( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    restoreDeletedRef(rRef.Ref1, rCxt);
    restoreDeletedRef(rRef.Ref2, rCxt);
}
 
enum ShrinkResult
{
    UNMODIFIED,
    SHRUNK,
    STICKY
};
 
ShrinkResult shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rDeletedRange,
        const ScComplexRefData& rRef )
{
    if (!rDeletedRange.Intersects(rRefRange))
        return UNMODIFIED;
 
    if (rCxt.mnColDelta < 0)
    {
        if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
            // Entire rows are not affected, columns are anchored.
            return STICKY;
 
        // Shifting left.
        if (rRefRange.aStart.Row() < rDeletedRange.aStart.Row() || rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
            // Deleted range is only partially overlapping in vertical direction. Bail out.
            return UNMODIFIED;
 
        if (rDeletedRange.aStart.Col() <= rRefRange.aStart.Col())
        {
            if (rRefRange.aEnd.Col() <= rDeletedRange.aEnd.Col())
            {
                // Reference is entirely deleted.
                rRefRange.SetInvalid();
            }
            else
            {
                // The reference range is truncated on the left.
                SCCOL nOffset = rDeletedRange.aStart.Col() - rRefRange.aStart.Col();
                SCCOL nDelta = rRefRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
                rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta+nOffset);
                rRefRange.aStart.IncCol(nOffset);
            }
        }
        else if (rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
        {
            if (rRefRange.IsEndColSticky(rCxt.mrDoc))
                // Sticky end not affected.
                return STICKY;
 
            // Reference is deleted in the middle. Move the last column
            // position to the left.
            SCCOL nDelta = rDeletedRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
            rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
        }
        else
        {
            if (rRefRange.IsEndColSticky(rCxt.mrDoc))
                // Sticky end not affected.
                return STICKY;
 
            // The reference range is truncated on the right.
            SCCOL nDelta = rDeletedRange.aStart.Col() - rRefRange.aEnd.Col() - 1;
            rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
        }
        return SHRUNK;
    }
    else if (rCxt.mnRowDelta < 0)
    {
        if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
            // Entire columns are not affected, rows are anchored.
            return STICKY;
 
        // Shifting up.
 
        if (rRefRange.aStart.Col() < rDeletedRange.aStart.Col() || rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
            // Deleted range is only partially overlapping in horizontal direction. Bail out.
            return UNMODIFIED;
 
        if (rDeletedRange.aStart.Row() <= rRefRange.aStart.Row())
        {
            if (rRefRange.aEnd.Row() <= rDeletedRange.aEnd.Row())
            {
                // Reference is entirely deleted.
                rRefRange.SetInvalid();
            }
            else
            {
                // The reference range is truncated on the top.
                SCROW nOffset = rDeletedRange.aStart.Row() - rRefRange.aStart.Row();
                SCROW nDelta = rRefRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
                rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta+nOffset);
                rRefRange.aStart.IncRow(nOffset);
            }
        }
        else if (rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
        {
            if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
                // Sticky end not affected.
                return STICKY;
 
            // Reference is deleted in the middle. Move the last row
            // position upward.
            SCROW nDelta = rDeletedRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
            rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
        }
        else
        {
            if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
                // Sticky end not affected.
                return STICKY;
 
            // The reference range is truncated on the bottom.
            SCROW nDelta = rDeletedRange.aStart.Row() - rRefRange.aEnd.Row() - 1;
            rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
        }
        return SHRUNK;
    }
 
    return UNMODIFIED;
}
 
bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
        const ScComplexRefData& rRef )
{
    if (!rSelectedRange.Intersects(rRefRange))
        return false;
 
    if (rCxt.mnColDelta > 0)
    {
        if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
            // Entire rows are not affected, columns are anchored.
            return false;
 
        // Insert and shifting right.
        if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
            // Selected range is only partially overlapping in vertical direction. Bail out.
            return false;
 
        if (rCxt.mrDoc.IsExpandRefs())
        {
            if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
                // Reference must be at least two columns wide.
                return false;
        }
        else
        {
            if (rSelectedRange.aStart.Col() <= rRefRange.aStart.Col())
                // Selected range is at the left end and the edge expansion is turned off.  No expansion.
                return false;
        }
 
        if (rRefRange.IsEndColSticky(rCxt.mrDoc))
            // Sticky end not affected.
            return false;
 
        // Move the last column position to the right.
        SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
        rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
        return true;
    }
    else if (rCxt.mnRowDelta > 0)
    {
        if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
            // Entire columns are not affected, rows are anchored.
            return false;
 
        // Insert and shifting down.
        if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
            // Selected range is only partially overlapping in horizontal direction. Bail out.
            return false;
 
        if (rCxt.mrDoc.IsExpandRefs())
        {
            if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
                // Reference must be at least two rows tall.
                return false;
        }
        else
        {
            if (rSelectedRange.aStart.Row() <= rRefRange.aStart.Row())
                // Selected range is at the top end and the edge expansion is turned off.  No expansion.
                return false;
        }
 
        if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
            // Sticky end not affected.
            return false;
 
        // Move the last row position down.
        SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
        rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
        return true;
    }
    return false;
}
 
/**
 * Check if the referenced range is expandable when the selected range is
 * not overlapping the referenced range.
 */
bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
        const ScComplexRefData& rRef )
{
    if (!rCxt.mrDoc.IsExpandRefs())
        // Edge-expansion is turned off.
        return false;
 
    if (rSelectedRange.aStart.Tab() > rRefRange.aStart.Tab() || rRefRange.aEnd.Tab() > rSelectedRange.aEnd.Tab())
        // Sheet references not within selected range.
        return false;
 
    if (rCxt.mnColDelta > 0)
    {
        if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
            // Entire rows are not affected, columns are anchored.
            return false;
 
        // Insert and shift right.
 
        if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
            // Reference must be at least two columns wide.
            return false;
 
        if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
            // Selected range is only partially overlapping in vertical direction. Bail out.
            return false;
 
        if (rSelectedRange.aStart.Col() - rRefRange.aEnd.Col() != 1)
            // Selected range is not immediately adjacent. Bail out.
            return false;
 
        if (rRefRange.IsEndColSticky(rCxt.mrDoc))
            // Sticky end not affected.
            return false;
 
        // Move the last column position to the right.
        SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
        rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
        return true;
    }
    else if (rCxt.mnRowDelta > 0)
    {
        if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
            // Entire columns are not affected, rows are anchored.
            return false;
 
        if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
            // Reference must be at least two rows tall.
            return false;
 
        if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
            // Selected range is only partially overlapping in horizontal direction. Bail out.
            return false;
 
        if (rSelectedRange.aStart.Row() - rRefRange.aEnd.Row() != 1)
            // Selected range is not immediately adjacent. Bail out.
            return false;
 
        if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
            // Sticky end not affected.
            return false;
 
        // Move the last row position down.
        SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
        rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
        return true;
    }
 
    return false;
}
 
bool isNameModified( const sc::UpdatedRangeNames& rUpdatedNames, SCTAB nOldTab, const formula::FormulaToken& rToken )
{
    SCTAB nTab = -1;
    if (rToken.GetSheet() >= 0)
        nTab = nOldTab;
 
    // Check if this named expression has been modified.
    return rUpdatedNames.isNameUpdated(nTab, rToken.GetIndex());
}
 
bool isDBDataModified( const ScDocument& rDoc, const formula::FormulaToken& rToken )
{
    // Check if this DBData has been modified.
    const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( rToken.GetIndex());
    if (!pDBData)
        return true;
 
    return pDBData->IsModified();
}
 
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos )
{
    ScRange aSelectedRange = getSelectedRange(rCxt);
 
    sc::RefUpdateResult aRes;
    ScAddress aNewPos = rOldPos;
    bool bCellShifted = rCxt.maRange.Contains(rOldPos);
    if (bCellShifted)
    {
        ScAddress aErrorPos( ScAddress::UNINITIALIZED );
        if (!aNewPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
        {
            assert(!"can't move");
        }
    }
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
 
                        if (rCxt.isDeleted() && aSelectedRange.Contains(aAbs))
                        {
                            // This reference is in the deleted region.
                            setRefDeleted(rRef, rCxt);
                            aRes.mbValueChanged = true;
                            break;
                        }
 
                        if (!rCxt.isDeleted() && rRef.IsDeleted())
                        {
                            // Check if the token has reference to previously deleted region.
                            ScAddress aCheckPos = rRef.toAbs(*mxSheetLimits, aNewPos);
                            if (rCxt.maRange.Contains(aCheckPos))
                            {
                                restoreDeletedRef(rRef, rCxt);
                                aRes.mbValueChanged = true;
                                break;
                            }
                        }
 
                        if (rCxt.maRange.Contains(aAbs))
                        {
                            ScAddress aErrorPos( ScAddress::UNINITIALIZED );
                            if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
                                aAbs = aErrorPos;
                            aRes.mbReferenceModified = true;
                        }
 
                        rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
 
                        if (rCxt.isDeleted())
                        {
                            if (aSelectedRange.Contains(aAbs))
                            {
                                // This reference is in the deleted region.
                                setRefDeleted(rRef, rCxt);
                                aRes.mbValueChanged = true;
                                break;
                            }
                            else if (aSelectedRange.Intersects(aAbs))
                            {
                                const ShrinkResult eSR = shrinkRange(rCxt, aAbs, aSelectedRange, rRef);
                                if (eSR == SHRUNK)
                                {
                                    // The reference range has been shrunk.
                                    rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                                    aRes.mbValueChanged = true;
                                    aRes.mbReferenceModified = true;
                                    break;
                                }
                                else if (eSR == STICKY)
                                {
                                    // The reference range stays the same but a
                                    // new (empty) cell range is shifted in and
                                    // may change the calculation result.
                                    aRes.mbValueChanged = true;
                                    // Sticky when intersecting the selected
                                    // range means also that the other
                                    // conditions below are not met,
                                    // specifically not the
                                    // if (rCxt.maRange.Contains(aAbs))
                                    // that is able to update the reference,
                                    // but aSelectedRange does not intersect
                                    // with rCxt.maRange so that can't happen
                                    // and we can bail out early without
                                    // updating the reference.
                                    break;
                                }
                            }
                        }
 
                        if (!rCxt.isDeleted() && rRef.IsDeleted())
                        {
                            // Check if the token has reference to previously deleted region.
                            ScRange aCheckRange = rRef.toAbs(*mxSheetLimits, aNewPos);
                            if (aSelectedRange.Contains(aCheckRange))
                            {
                                // This reference was previously in the deleted region. Restore it.
                                restoreDeletedRef(rRef, rCxt);
                                aRes.mbValueChanged = true;
                                break;
                            }
                        }
 
                        if (rCxt.isInserted())
                        {
                            if (expandRange(rCxt, aAbs, aSelectedRange, rRef))
                            {
                                // The reference range has been expanded.
                                rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                                aRes.mbValueChanged = true;
                                aRes.mbReferenceModified = true;
                                break;
                            }
 
                            if (expandRangeByEdge(rCxt, aAbs, aSelectedRange, rRef))
                            {
                                // The reference range has been expanded on the edge.
                                rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                                aRes.mbValueChanged = true;
                                aRes.mbReferenceModified = true;
                                break;
                            }
                        }
 
                        if (rCxt.maRange.Contains(aAbs))
                        {
                            // We shift either by column or by row, not both,
                            // so moving the reference has only to be done in
                            // the non-sticky case.
                            if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
                                || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
                            {
                                // In entire col/row, values are shifted within
                                // the reference, which affects all positional
                                // results like in MATCH or matrix positions.
                                aRes.mbValueChanged = true;
                            }
                            else
                            {
                                ScRange aErrorRange( ScAddress::UNINITIALIZED );
                                if (!aAbs.MoveSticky(rCxt.mrDoc, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange))
                                    aAbs = aErrorRange;
                                aRes.mbReferenceModified = true;
                            }
                        }
                        else if (rCxt.maRange.Intersects(aAbs))
                        {
                            // Part of the referenced range is being shifted. This
                            // will change the values of the range.
                            aRes.mbValueChanged = true;
                        }
 
                        rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                case svExternalSingleRef:
                    {
                        // For external reference, just reset the reference with
                        // respect to the new cell position.
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                case svExternalDoubleRef:
                    {
                        // Same as above.
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                default:
                    ;
            }
 
            // For ocTableRef p is the inner token of *pp, so have a separate
            // condition here.
            if ((*pp)->GetType() == svIndex)
            {
                switch ((*pp)->GetOpCode())
                {
                    case ocName:
                        {
                            SCTAB nOldTab = (*pp)->GetSheet();
                            if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
                                aRes.mbNameModified = true;
                            if (rCxt.mnTabDelta &&
                                    rCxt.maRange.aStart.Tab() <= nOldTab && nOldTab <= rCxt.maRange.aEnd.Tab())
                            {
                                aRes.mbNameModified = true;
                                (*pp)->SetSheet( nOldTab + rCxt.mnTabDelta);
                            }
                        }
                        break;
                    case ocDBArea:
                    case ocTableRef:
                        if (isDBDataModified(rCxt.mrDoc, **pp))
                            aRes.mbNameModified = true;
                        break;
                    default:
                        ;   // nothing
                }
            }
        }
    }
 
    return aRes;
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
    const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos, const ScAddress& rNewPos )
{
    sc::RefUpdateResult aRes;
 
    if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
        // The cell hasn't moved at all.
        return aRes;
 
    // When moving, the range in the context is the destination range. We need
    // to use the old range prior to the move for hit analysis.
    ScRange aOldRange = rCxt.maRange;
    ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
    if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
    {
        assert(!"can't move");
    }
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
 
                        // Do not update the reference in transposed case (cut paste transposed).
                        // The reference will be updated in UpdateTranspose().
                        // Additionally, do not update the references from cells within the moved
                        // range as they lead to #REF! errors here. These #REF! cannot by fixed
                        // later in UpdateTranspose().
                        if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
                            break;
 
                        if (aOldRange.Contains(aAbs))
                        {
                            ScAddress aErrorPos( ScAddress::UNINITIALIZED );
                            if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
                                aAbs = aErrorPos;
                            aRes.mbReferenceModified = true;
                        }
                        else if (rCxt.maRange.Contains(aAbs))
                        {
                            // Referenced cell has been overwritten.
                            aRes.mbValueChanged = true;
                        }
 
                        rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
                        rRef.SetFlag3D(rRef.IsFlag3D() || !rRef.IsTabRel() || aAbs.Tab() != rNewPos.Tab());
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
 
                        // Do not update the reference in transposed case (cut paste transposed).
                        // The reference will be updated in UpdateTranspose().
                        // Additionally, do not update the references from cells within the moved
                        // range as they lead to #REF! errors here. These #REF! cannot by fixed
                        // later in UpdateTranspose().
                        if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
                            break;
 
                        if (aOldRange.Contains(aAbs))
                        {
                            ScRange aErrorRange( ScAddress::UNINITIALIZED );
                            if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
                                aAbs = aErrorRange;
                            aRes.mbReferenceModified = true;
                        }
                        else if (rCxt.maRange.Contains(aAbs))
                        {
                            // Referenced range has been entirely overwritten.
                            aRes.mbValueChanged = true;
                        }
 
                        rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
                        bool b1, b2;
                        if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
                        {
                            // More than one sheet referenced => has to have
                            // both 3D flags.
                            b1 = b2 = true;
                        }
                        else
                        {
                            // Keep given 3D flag even for relative sheet
                            // reference to same sheet.
                            // Absolute sheet reference => set 3D flag.
                            // Reference to another sheet => set 3D flag.
                            b1 = rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel() || rNewPos.Tab() != aAbs.aStart.Tab();
                            b2 = rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel() || rNewPos.Tab() != aAbs.aEnd.Tab();
                            // End part has 3D flag => start part must have it too.
                            if (b2)
                                b1 = true;
                            // End part sheet reference is identical to start
                            // part sheet reference and end part sheet
                            // reference was not explicitly given => clear end
                            // part 3D flag.
                            if (b1 && b2 && rRef.Ref1.IsTabRel() == rRef.Ref2.IsTabRel() && !rRef.Ref2.IsFlag3D())
                                b2 = false;
                        }
                        rRef.Ref1.SetFlag3D(b1);
                        rRef.Ref2.SetFlag3D(b2);
                    }
                    break;
                case svExternalSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                case svExternalDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                default:
                    ;
            }
 
            // For ocTableRef p is the inner token of *pp, so have a separate
            // condition here.
            if ((*pp)->GetType() == svIndex)
            {
                switch ((*pp)->GetOpCode())
                {
                    case ocName:
                        {
                            SCTAB nOldTab = (*pp)->GetSheet();
                            if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
                                aRes.mbNameModified = true;
                        }
                        break;
                    case ocDBArea:
                    case ocTableRef:
                        if (isDBDataModified(rCxt.mrDoc, **pp))
                            aRes.mbNameModified = true;
                        break;
                    default:
                        ;   // nothing
                }
            }
        }
    }
 
    return aRes;
}
 
void ScTokenArray::MoveReferenceColReorder(
    const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColRowReorderMapType& rColMap )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
 
                        if (aAbs.Tab() == nTab && nRow1 <= aAbs.Row() && aAbs.Row() <= nRow2)
                        {
                            // Inside reordered row range.
                            sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.Col());
                            if (it != rColMap.end())
                            {
                                // This column is reordered.
                                SCCOL nNewCol = it->second;
                                aAbs.SetCol(nNewCol);
                                rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
                            }
                        }
                    }
                break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
 
                        if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
                            // Must be a single-sheet reference.
                            break;
 
                        if (aAbs.aStart.Col() != aAbs.aEnd.Col())
                            // Whole range must fit in a single column.
                            break;
 
                        if (aAbs.aStart.Tab() == nTab && nRow1 <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= nRow2)
                        {
                            // Inside reordered row range.
                            sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.aStart.Col());
                            if (it != rColMap.end())
                            {
                                // This column is reordered.
                                SCCOL nNewCol = it->second;
                                aAbs.aStart.SetCol(nNewCol);
                                aAbs.aEnd.SetCol(nNewCol);
                                rRef.SetRange(*mxSheetLimits, aAbs, rPos);
                            }
                        }
                    }
                break;
                default:
                    ;
            }
        }
    }
}
 
void ScTokenArray::MoveReferenceRowReorder( const ScAddress& rPos, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, const sc::ColRowReorderMapType& rRowMap )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
 
                        if (aAbs.Tab() == nTab && nCol1 <= aAbs.Col() && aAbs.Col() <= nCol2)
                        {
                            // Inside reordered column range.
                            sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.Row());
                            if (it != rRowMap.end())
                            {
                                // This column is reordered.
                                SCROW nNewRow = it->second;
                                aAbs.SetRow(nNewRow);
                                rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
                            }
                        }
                    }
                break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
 
                        if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
                            // Must be a single-sheet reference.
                            break;
 
                        if (aAbs.aStart.Row() != aAbs.aEnd.Row())
                            // Whole range must fit in a single row.
                            break;
 
                        if (aAbs.aStart.Tab() == nTab && nCol1 <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= nCol2)
                        {
                            // Inside reordered column range.
                            sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.aStart.Row());
                            if (it != rRowMap.end())
                            {
                                // This row is reordered.
                                SCROW nNewRow = it->second;
                                aAbs.aStart.SetRow(nNewRow);
                                aAbs.aEnd.SetRow(nNewRow);
                                rRef.SetRange(*mxSheetLimits, aAbs, rPos);
                            }
                        }
                    }
                break;
                default:
                    ;
            }
        }
    }
}
 
namespace {
 
bool adjustSingleRefInName(
    ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos,
    ScComplexRefData* pEndOfComplex )
{
    ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
    if (aAbs.Tab() < rCxt.maRange.aStart.Tab() || rCxt.maRange.aEnd.Tab() < aAbs.Tab())
    {
        // This references a sheet that has not shifted. Don't change it.
        return false;
    }
 
    if (!rCxt.maRange.Contains(rRef.toAbs(rCxt.mrDoc, rPos)))
        return false;
 
    bool bChanged = false;
 
    if (rCxt.mnColDelta && !rRef.IsColRel())
    {
        // Adjust absolute column reference.
        if (rCxt.maRange.aStart.Col() <= rRef.Col() && rRef.Col() <= rCxt.maRange.aEnd.Col())
        {
            if (pEndOfComplex)
            {
                if (pEndOfComplex->IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos))
                    bChanged = true;
            }
            else
            {
                rRef.IncCol(rCxt.mnColDelta);
                bChanged = true;
            }
        }
    }
 
    if (rCxt.mnRowDelta && !rRef.IsRowRel())
    {
        // Adjust absolute row reference.
        if (rCxt.maRange.aStart.Row() <= rRef.Row() && rRef.Row() <= rCxt.maRange.aEnd.Row())
        {
            if (pEndOfComplex)
            {
                if (pEndOfComplex->IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos))
                    bChanged = true;
            }
            else
            {
                rRef.IncRow(rCxt.mnRowDelta);
                bChanged = true;
            }
        }
    }
 
    if (!rRef.IsTabRel() && rCxt.mnTabDelta)
    {
        // Sheet range has already been checked above.
        rRef.IncTab(rCxt.mnTabDelta);
        bChanged = true;
    }
 
    return bChanged;
}
 
bool adjustDoubleRefInName(
    ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
{
    bool bRefChanged = false;
    if (rCxt.mrDoc.IsExpandRefs())
    {
        if (rCxt.mnRowDelta > 0 && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel())
        {
            ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
            // Expand only if at least two rows tall.
            if (aAbs.aStart.Row() < aAbs.aEnd.Row())
            {
                // Check and see if we should expand the range at the top.
                ScRange aSelectedRange = getSelectedRange(rCxt);
                if (aSelectedRange.Intersects(aAbs))
                {
                    // Selection intersects the referenced range. Only expand the
                    // bottom position.
                    rRef.IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos);
                    return true;
                }
            }
        }
        if (rCxt.mnColDelta > 0 && !rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel())
        {
            ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
            // Expand only if at least two columns wide.
            if (aAbs.aStart.Col() < aAbs.aEnd.Col())
            {
                // Check and see if we should expand the range at the left.
                ScRange aSelectedRange = getSelectedRange(rCxt);
                if (aSelectedRange.Intersects(aAbs))
                {
                    // Selection intersects the referenced range. Only expand the
                    // right position.
                    rRef.IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos);
                    return true;
                }
            }
        }
    }
 
    if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
        || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
    {
        sc::RefUpdateContext aCxt( rCxt.mrDoc);
        // We only need a few parameters of RefUpdateContext.
        aCxt.maRange = rCxt.maRange;
        aCxt.mnColDelta = rCxt.mnColDelta;
        aCxt.mnRowDelta = rCxt.mnRowDelta;
        aCxt.mnTabDelta = rCxt.mnTabDelta;
 
        // References to entire col/row are not to be adjusted in the other axis.
        if (aCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
            aCxt.mnRowDelta = 0;
        if (aCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
            aCxt.mnColDelta = 0;
        if (!aCxt.mnColDelta && !aCxt.mnRowDelta && !aCxt.mnTabDelta)
            // early bailout
            return bRefChanged;
 
        // Ref2 before Ref1 for sticky ends.
        if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos, &rRef))
            bRefChanged = true;
 
        if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos, nullptr))
            bRefChanged = true;
    }
    else
    {
        // Ref2 before Ref1 for sticky ends.
        if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos, &rRef))
            bRefChanged = true;
 
        if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos, nullptr))
            bRefChanged = true;
    }
 
    return bRefChanged;
}
 
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceInName(
    const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
{
    if (rCxt.meMode == URM_MOVE)
        return AdjustReferenceInMovedName(rCxt, rPos);
 
    sc::RefUpdateResult aRes;
 
    if (rCxt.meMode == URM_COPY)
        // Copying cells does not modify named expressions.
        return aRes;
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        if (rCxt.mnRowDelta < 0)
                        {
                            // row(s) deleted.
 
                            if (rRef.IsRowRel())
                                // Don't modify relative references in names.
                                break;
 
                            ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
                            if (aAbs.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.Col())
                                // column of the reference is not in the deleted column range.
                                break;
 
                            if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
                                // wrong tables
                                break;
 
                            const SCROW nDelStartRow = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
                            const SCROW nDelEndRow = nDelStartRow - rCxt.mnRowDelta - 1;
 
                            if (nDelStartRow <= aAbs.Row() && aAbs.Row() <= nDelEndRow)
                            {
                                // This reference is deleted.
                                rRef.SetRowDeleted(true);
                                aRes.mbReferenceModified = true;
                                break;
                            }
                        }
                        else if (rCxt.mnColDelta < 0)
                        {
                            // column(s) deleted.
 
                            if (rRef.IsColRel())
                                // Don't modify relative references in names.
                                break;
 
                            ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
                            if (aAbs.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.Row())
                                // row of the reference is not in the deleted row range.
                                break;
 
                            if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
                                // wrong tables
                                break;
 
                            const SCCOL nDelStartCol = rCxt.maRange.aStart.Col() + rCxt.mnColDelta;
                            const SCCOL nDelEndCol = nDelStartCol - rCxt.mnColDelta - 1;
 
                            if (nDelStartCol <= aAbs.Col() && aAbs.Col() <= nDelEndCol)
                            {
                                // This reference is deleted.
                                rRef.SetColDeleted(true);
                                aRes.mbReferenceModified = true;
                                break;
                            }
                        }
 
                        if (adjustSingleRefInName(rRef, rCxt, rPos, nullptr))
                            aRes.mbReferenceModified = true;
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
                        if (aAbs.aStart.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.aEnd.Tab() < rCxt.maRange.aStart.Tab())
                            // Sheet references not affected.
                            break;
 
                        if (rCxt.maRange.Contains(aAbs))
                        {
                            // This range is entirely within the shifted region.
                            if (adjustDoubleRefInName(rRef, rCxt, rPos))
                                aRes.mbReferenceModified = true;
                        }
                        else if (rCxt.mnRowDelta < 0)
                        {
                            // row(s) deleted.
 
                            if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
                                // Rows of entire columns are not affected.
                                break;
 
                            if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
                                // Don't modify relative references in names.
                                break;
 
                            if (aAbs.aStart.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.aEnd.Col())
                                // column range of the reference is not entirely in the deleted column range.
                                break;
 
                            ScRange aDeleted = rCxt.maRange;
                            aDeleted.aStart.IncRow(rCxt.mnRowDelta);
                            aDeleted.aEnd.SetRow(aDeleted.aStart.Row()-rCxt.mnRowDelta-1);
 
                            if (aAbs.aEnd.Row() < aDeleted.aStart.Row() || aDeleted.aEnd.Row() < aAbs.aStart.Row())
                                // reference range doesn't intersect with the deleted range.
                                break;
 
                            if (aDeleted.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= aDeleted.aEnd.Row())
                            {
                                // This reference is entirely deleted.
                                rRef.Ref1.SetRowDeleted(true);
                                rRef.Ref2.SetRowDeleted(true);
                                aRes.mbReferenceModified = true;
                                break;
                            }
 
                            if (aAbs.aStart.Row() < aDeleted.aStart.Row())
                            {
                                if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
                                {
                                    if (aDeleted.aEnd.Row() < aAbs.aEnd.Row())
                                        // Deleted in the middle.  Make the reference shorter.
                                        rRef.Ref2.IncRow(rCxt.mnRowDelta);
                                    else
                                        // Deleted at tail end.  Cut off the lower part.
                                        rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1);
                                }
                            }
                            else
                            {
                                // Deleted at the top.  Cut the top off and shift up.
                                rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1);
                                rRef.Ref1.IncRow(rCxt.mnRowDelta);
                                if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
                                    rRef.Ref2.IncRow(rCxt.mnRowDelta);
                            }
 
                            aRes.mbReferenceModified = true;
                        }
                        else if (rCxt.mnColDelta < 0)
                        {
                            // column(s) deleted.
 
                            if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
                                // Rows of entire rows are not affected.
                                break;
 
                            if (rRef.Ref1.IsColRel() || rRef.Ref2.IsColRel())
                                // Don't modify relative references in names.
                                break;
 
                            if (aAbs.aStart.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.aEnd.Row())
                                // row range of the reference is not entirely in the deleted row range.
                                break;
 
                            ScRange aDeleted = rCxt.maRange;
                            aDeleted.aStart.IncCol(rCxt.mnColDelta);
                            aDeleted.aEnd.SetCol(aDeleted.aStart.Col()-rCxt.mnColDelta-1);
 
                            if (aAbs.aEnd.Col() < aDeleted.aStart.Col() || aDeleted.aEnd.Col() < aAbs.aStart.Col())
                                // reference range doesn't intersect with the deleted range.
                                break;
 
                            if (aDeleted.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= aDeleted.aEnd.Col())
                            {
                                // This reference is entirely deleted.
                                rRef.Ref1.SetColDeleted(true);
                                rRef.Ref2.SetColDeleted(true);
                                aRes.mbReferenceModified = true;
                                break;
                            }
 
                            if (aAbs.aStart.Col() < aDeleted.aStart.Col())
                            {
                                if (!aAbs.IsEndColSticky(rCxt.mrDoc))
                                {
                                    if (aDeleted.aEnd.Col() < aAbs.aEnd.Col())
                                        // Deleted in the middle.  Make the reference shorter.
                                        rRef.Ref2.IncCol(rCxt.mnColDelta);
                                    else
                                        // Deleted at tail end.  Cut off the right part.
                                        rRef.Ref2.SetAbsCol(aDeleted.aStart.Col()-1);
                                }
                            }
                            else
                            {
                                // Deleted at the left.  Cut the left off and shift left.
                                rRef.Ref1.SetAbsCol(aDeleted.aEnd.Col()+1);
                                rRef.Ref1.IncCol(rCxt.mnColDelta);
                                if (!aAbs.IsEndColSticky(rCxt.mrDoc))
                                    rRef.Ref2.IncCol(rCxt.mnColDelta);
                            }
 
                            aRes.mbReferenceModified = true;
                        }
                        else if (rCxt.maRange.Intersects(aAbs))
                        {
                            if (rCxt.mnColDelta && rCxt.maRange.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= rCxt.maRange.aEnd.Row())
                            {
                                if (adjustDoubleRefInName(rRef, rCxt, rPos))
                                    aRes.mbReferenceModified = true;
                            }
                            if (rCxt.mnRowDelta && rCxt.maRange.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= rCxt.maRange.aEnd.Col())
                            {
                                if (adjustDoubleRefInName(rRef, rCxt, rPos))
                                    aRes.mbReferenceModified = true;
                            }
                        }
                        else if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
                        {
                            // Check if we could expand range reference by the bottom
                            // edge. For named expressions, we only expand absolute
                            // references. Reference must be at least two rows
                            // tall.
                            if (!rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel() &&
                                    aAbs.aStart.Row() < aAbs.aEnd.Row() &&
                                    aAbs.aEnd.Row()+1 == rCxt.maRange.aStart.Row())
                            {
                                // Expand by the bottom edge.
                                rRef.Ref2.IncRow(rCxt.mnRowDelta);
                                aRes.mbReferenceModified = true;
                            }
                        }
                        else if (rCxt.mnColDelta > 0 && rCxt.mrDoc.IsExpandRefs())
                        {
                            // Check if we could expand range reference by the right
                            // edge. For named expressions, we only expand absolute
                            // references. Reference must be at least two
                            // columns wide.
                            if (!rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel() &&
                                    aAbs.aStart.Col() < aAbs.aEnd.Col() &&
                                    aAbs.aEnd.Col()+1 == rCxt.maRange.aStart.Col())
                            {
                                // Expand by the right edge.
                                rRef.Ref2.IncCol(rCxt.mnColDelta);
                                aRes.mbReferenceModified = true;
                            }
                        }
                    }
                    break;
                default:
                    ;
            }
        }
    }
 
    return aRes;
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
{
    // When moving, the range is the destination range.
    ScRange aOldRange = rCxt.maRange;
    ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
    if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
    {
        assert(!"can't move");
    }
 
    // In a named expression, we'll move the reference only when the reference
    // is entirely absolute.
 
    sc::RefUpdateResult aRes;
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        if (rRef.IsColRel() || rRef.IsRowRel() || rRef.IsTabRel())
                            continue;
 
                        ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
                        // Do not update the reference in transposed case (cut paste transposed).
                        // The reference will be updated in UpdateTranspose().
                        if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
                            break;
 
                        if (aOldRange.Contains(aAbs))
                        {
                            ScAddress aErrorPos( ScAddress::UNINITIALIZED );
                            if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
                                aAbs = aErrorPos;
                            aRes.mbReferenceModified = true;
                        }
 
                        rRef.SetAddress(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        if (rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() || rRef.Ref1.IsTabRel() ||
                                rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() || rRef.Ref2.IsTabRel())
                            continue;
 
                        ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
 
                        // Do not update the reference in transposed case (cut paste transposed).
                        // The reference will be updated in UpdateTranspose().
                        if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
                            break;
 
                        if (aOldRange.Contains(aAbs))
                        {
                            ScRange aErrorRange( ScAddress::UNINITIALIZED );
                            if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
                                aAbs = aErrorRange;
                            aRes.mbReferenceModified = true;
                        }
 
                        rRef.SetRange(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
                    }
                    break;
                default:
                    ;
            }
        }
    }
 
    return aRes;
}
 
namespace {
 
bool adjustSingleRefOnDeletedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
{
    ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
    if (nDelPos <= aAbs.Tab() && aAbs.Tab() < nDelPos + nSheets)
    {
        rRef.SetTabDeleted(true);
        return true;
    }
 
    if (nDelPos < aAbs.Tab())
    {
        // Reference sheet needs to be adjusted.
        aAbs.IncTab(-1*nSheets);
        rRef.SetAddress(rLimits, aAbs, rNewPos);
        return true;
    }
    else if (rOldPos.Tab() != rNewPos.Tab())
    {
        // Cell itself has moved.
        rRef.SetAddress(rLimits, aAbs, rNewPos);
        return true;
    }
 
    return false;
}
 
bool adjustSingleRefOnInsertedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nInsPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
{
    ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
    if (nInsPos <= aAbs.Tab())
    {
        // Reference sheet needs to be adjusted.
        aAbs.IncTab(nSheets);
        rRef.SetAddress(rLimits, aAbs, rNewPos);
        return true;
    }
    else if (rOldPos.Tab() != rNewPos.Tab())
    {
        // Cell itself has moved.
        rRef.SetAddress(rLimits, aAbs, rNewPos);
        return true;
    }
 
    return false;
}
 
bool adjustDoubleRefOnDeleteTab(const ScSheetLimits& rLimits, ScComplexRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos)
{
    ScSingleRefData& rRef1 = rRef.Ref1;
    ScSingleRefData& rRef2 = rRef.Ref2;
    ScAddress aStartPos = rRef1.toAbs(rLimits, rOldPos);
    ScAddress aEndPos = rRef2.toAbs(rLimits, rOldPos);
    bool bMoreThanOneTab = aStartPos.Tab() != aEndPos.Tab();
    bool bModified = false;
    if (bMoreThanOneTab && aStartPos.Tab() == nDelPos && nDelPos + nSheets <= aEndPos.Tab())
    {
        if (rRef1.IsTabRel() && aStartPos.Tab() < rOldPos.Tab())
        {
            rRef1.IncTab(nSheets);
            bModified = true;
        }
    }
    else
    {
        bModified = adjustSingleRefOnDeletedTab(rLimits, rRef1, nDelPos, nSheets, rOldPos, rNewPos);
    }
 
    if (bMoreThanOneTab && aEndPos.Tab() == nDelPos && aStartPos.Tab() <= nDelPos - nSheets)
    {
        if (!rRef2.IsTabRel() || rOldPos.Tab() < aEndPos.Tab())
        {
            rRef2.IncTab(-nSheets);
            bModified = true;
        }
    }
    else
    {
        bModified |= adjustSingleRefOnDeletedTab(rLimits, rRef2, nDelPos, nSheets, rOldPos, rNewPos);
    }
    return bModified;
}
 
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnDeletedTab( const sc::RefUpdateDeleteTabContext& rCxt, const ScAddress& rOldPos )
{
    sc::RefUpdateResult aRes;
    ScAddress aNewPos = rOldPos;
    ScRangeUpdater::UpdateDeleteTab( aNewPos, rCxt);
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        if (adjustSingleRefOnDeletedTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos))
                            aRes.mbReferenceModified = true;
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        aRes.mbReferenceModified |= adjustDoubleRefOnDeleteTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos);
                    }
                    break;
                default:
                    ;
            }
 
            // For ocTableRef p is the inner token of *pp, so have a separate
            // condition here.
            if ((*pp)->GetType() == svIndex)
            {
                switch ((*pp)->GetOpCode())
                {
                    case ocName:
                        {
                            SCTAB nOldTab = (*pp)->GetSheet();
                            if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
                                aRes.mbNameModified = true;
                            if (rCxt.mnDeletePos <= nOldTab)
                            {
                                aRes.mbNameModified = true;
                                if (rCxt.mnDeletePos + rCxt.mnSheets <= nOldTab)
                                    (*pp)->SetSheet( nOldTab - rCxt.mnSheets);
                                else
                                    // Would point to a deleted sheet. Invalidate.
                                    (*pp)->SetSheet( SCTAB_MAX);
                            }
                        }
                        break;
                    case ocDBArea:
                    case ocTableRef:
                        if (isDBDataModified(rCxt.mrDoc, **pp))
                            aRes.mbNameModified = true;
                        break;
                    default:
                        ;   // nothing
                }
            }
        }
    }
    return aRes;
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnInsertedTab( const sc::RefUpdateInsertTabContext& rCxt, const ScAddress& rOldPos )
{
    sc::RefUpdateResult aRes;
    ScAddress aNewPos = rOldPos;
    if (rCxt.mnInsertPos <= rOldPos.Tab())
        aNewPos.IncTab(rCxt.mnSheets);
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
                            aRes.mbReferenceModified = true;
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref1, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
                            aRes.mbReferenceModified = true;
                        if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref2, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
                            aRes.mbReferenceModified = true;
                    }
                    break;
                default:
                    ;
            }
 
            // For ocTableRef p is the inner token of *pp, so have a separate
            // condition here.
            if ((*pp)->GetType() == svIndex)
            {
                switch ((*pp)->GetOpCode())
                {
                    case ocName:
                        {
                            SCTAB nOldTab = (*pp)->GetSheet();
                            if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
                                aRes.mbNameModified = true;
                            if (rCxt.mnInsertPos <= nOldTab)
                            {
                                aRes.mbNameModified = true;
                                (*pp)->SetSheet( nOldTab + rCxt.mnSheets);
                            }
                        }
                        break;
                    case ocDBArea:
                    case ocTableRef:
                        if (isDBDataModified(rCxt.mrDoc, **pp))
                            aRes.mbNameModified = true;
                        break;
                    default:
                        ;   // nothing
                }
            }
        }
    }
    return aRes;
}
 
namespace {
 
bool adjustTabOnMove( ScAddress& rPos, const sc::RefUpdateMoveTabContext& rCxt )
{
    SCTAB nNewTab = rCxt.getNewTab(rPos.Tab());
    if (nNewTab == rPos.Tab())
        return false;
 
    rPos.SetTab(nNewTab);
    return true;
}
 
}
 
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMovedTab( const sc::RefUpdateMoveTabContext& rCxt, const ScAddress& rOldPos )
{
    sc::RefUpdateResult aRes;
    if (rCxt.mnOldPos == rCxt.mnNewPos)
        return aRes;
 
    ScAddress aNewPos = rOldPos;
    if (adjustTabOnMove(aNewPos, rCxt))
    {
        aRes.mbReferenceModified = true;
        aRes.mbValueChanged = true;
        aRes.mnTab = aNewPos.Tab(); // this sets the new tab position used when deleting
    }
 
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        if (adjustTabOnMove(aAbs, rCxt))
                            aRes.mbReferenceModified = true;
                        rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        if (adjustTabOnMove(aAbs.aStart, rCxt))
                            aRes.mbReferenceModified = true;
                        if (adjustTabOnMove(aAbs.aEnd, rCxt))
                            aRes.mbReferenceModified = true;
                        rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
                    }
                    break;
                default:
                    ;
            }
 
            // For ocTableRef p is the inner token of *pp, so have a separate
            // condition here.
            if ((*pp)->GetType() == svIndex)
            {
                switch ((*pp)->GetOpCode())
                {
                    case ocName:
                        {
                            SCTAB nOldTab = (*pp)->GetSheet();
                            if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
                                aRes.mbNameModified = true;
                            SCTAB nNewTab = rCxt.getNewTab( nOldTab);
                            if (nNewTab != nOldTab)
                            {
                                aRes.mbNameModified = true;
                                (*pp)->SetSheet( nNewTab);
                            }
                        }
                        break;
                    case ocDBArea:
                    case ocTableRef:
                        if (isDBDataModified(rCxt.mrDoc, **pp))
                            aRes.mbNameModified = true;
                        break;
                    default:
                        ;   // nothing
                }
            }
        }
    }
 
    return aRes;
}
 
void ScTokenArray::AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                case svExternalSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                case svDoubleRef:
                case svExternalDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
void ScTokenArray::AdjustReferenceOnMovedOriginIfOtherSheet( const ScAddress& rOldPos, const ScAddress& rNewPos )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            bool bAdjust = false;
            switch (p->GetType())
            {
                case svExternalSingleRef:
                    bAdjust = true;     // always
                    [[fallthrough]];
                case svSingleRef:
                    {
                        ScSingleRefData& rRef = *p->GetSingleRef();
                        ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        if (!bAdjust)
                            bAdjust = (aAbs.Tab() != rOldPos.Tab());
                        if (bAdjust)
                            rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                case svExternalDoubleRef:
                    bAdjust = true;     // always
                    [[fallthrough]];
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
                        if (!bAdjust)
                            bAdjust = (rOldPos.Tab() < aAbs.aStart.Tab() || aAbs.aEnd.Tab() < rOldPos.Tab());
                        if (bAdjust)
                            rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
void ScTokenArray::AdjustReferenceOnCopy( const ScAddress& rNewPos )
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svDoubleRef:
                    {
                        ScComplexRefData& rRef = *p->GetDoubleRef();
                        rRef.PutInOrder( rNewPos);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
namespace {
 
void clearTabDeletedFlag( const ScSheetLimits& rLimits, ScSingleRefData& rRef, const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
{
    if (!rRef.IsTabDeleted())
        return;
 
    ScAddress aAbs = rRef.toAbs(rLimits, rPos);
    if (nStartTab <=  aAbs.Tab() && aAbs.Tab() <= nEndTab)
        rRef.SetTabDeleted(false);
}
 
}
 
void ScTokenArray::ClearTabDeleted( const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
{
    if (nEndTab < nStartTab)
        return;
 
    FormulaToken** p = pCode.get();
    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
    for (; p != pEnd; ++p)
    {
        switch ((*p)->GetType())
        {
            case svSingleRef:
            {
                formula::FormulaToken* pToken = *p;
                ScSingleRefData& rRef = *pToken->GetSingleRef();
                clearTabDeletedFlag(*mxSheetLimits, rRef, rPos, nStartTab, nEndTab);
            }
            break;
            case svDoubleRef:
            {
                formula::FormulaToken* pToken = *p;
                ScComplexRefData& rRef = *pToken->GetDoubleRef();
                clearTabDeletedFlag(*mxSheetLimits, rRef.Ref1, rPos, nStartTab, nEndTab);
                clearTabDeletedFlag(*mxSheetLimits, rRef.Ref2, rPos, nStartTab, nEndTab);
            }
            break;
            default:
                ;
        }
    }
}
 
namespace {
 
void checkBounds(
    const ScSheetLimits& rLimits,
    const ScAddress& rPos, SCROW nGroupLen, const ScRange& rCheckRange,
    const ScSingleRefData& rRef, std::vector<SCROW>& rBounds, const ScRange* pDeletedRange )
{
    if (!rRef.IsRowRel())
        return;
 
    ScRange aAbs(rRef.toAbs(rLimits, rPos));
    aAbs.aEnd.IncRow(nGroupLen-1);
    if (!rCheckRange.Intersects(aAbs) && (!pDeletedRange || !pDeletedRange->Intersects(aAbs)))
        return;
 
    // Get the boundary row positions.
    if (aAbs.aEnd.Row() < rCheckRange.aStart.Row() && (!pDeletedRange || aAbs.aEnd.Row() < pDeletedRange->aStart.Row()))
        // No intersections.
        return;
 
    // rCheckRange may be a virtual non-existent row being shifted in.
    if (aAbs.aStart.Row() <= rCheckRange.aStart.Row() && rCheckRange.aStart.Row() < rLimits.GetMaxRowCount())
    {
        //    +-+ <---- top
        //    | |
        // +--+-+--+ <---- boundary row position
        // |  | |  |
        // |       |
        // +-------+
 
        // Add offset from the reference top to the cell position.
        SCROW nOffset = rCheckRange.aStart.Row() - aAbs.aStart.Row();
        rBounds.push_back(rPos.Row()+nOffset);
    }
    // Same for deleted range.
    if (pDeletedRange && aAbs.aStart.Row() <= pDeletedRange->aStart.Row())
    {
        SCROW nOffset = pDeletedRange->aStart.Row() - aAbs.aStart.Row();
        SCROW nRow = rPos.Row() + nOffset;
        // Unlike for rCheckRange, for pDeletedRange nRow can be anywhere>=0.
        if (rLimits.ValidRow(nRow))
            rBounds.push_back(nRow);
    }
 
    if (aAbs.aEnd.Row() >= rCheckRange.aEnd.Row())
    {
        // only check for end range
 
        // +-------+
        // |       |
        // |  | |  |
        // +--+-+--+ <---- boundary row position
        //    | |
        //    +-+
 
        // Ditto.
        SCROW nOffset = rCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
        rBounds.push_back(rPos.Row()+nOffset);
    }
    // Same for deleted range.
    if (pDeletedRange && aAbs.aEnd.Row() >= pDeletedRange->aEnd.Row())
    {
        SCROW nOffset = pDeletedRange->aEnd.Row() + 1 - aAbs.aStart.Row();
        SCROW nRow = rPos.Row() + nOffset;
        // Unlike for rCheckRange, for pDeletedRange nRow can be ~anywhere.
        if (rLimits.ValidRow(nRow))
            rBounds.push_back(nRow);
    }
}
 
void checkBounds(
    const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
    const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
{
    if (!rRef.IsRowRel())
        return;
 
    ScRange aDeletedRange( ScAddress::UNINITIALIZED );
    const ScRange* pDeletedRange = nullptr;
 
    ScRange aCheckRange = rCxt.maRange;
    if (rCxt.meMode == URM_MOVE)
    {
        // Check bounds against the old range prior to the move.
        ScRange aErrorRange( ScAddress::UNINITIALIZED );
        if (!aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
        {
            assert(!"can't move");
        }
 
        // Check bounds also against the range moved into.
        pDeletedRange = &rCxt.maRange;
    }
    else if (rCxt.meMode == URM_INSDEL &&
            ((rCxt.mnColDelta < 0 && rCxt.maRange.aStart.Col() > 0) ||
             (rCxt.mnRowDelta < 0 && rCxt.maRange.aStart.Row() > 0)))
    {
        // Check bounds also against deleted range where cells are shifted
        // into and references need to be invalidated.
        aDeletedRange = getSelectedRange( rCxt);
        pDeletedRange = &aDeletedRange;
    }
 
    checkBounds(rCxt.mrDoc.GetSheetLimits(), rPos, nGroupLen, aCheckRange, rRef, rBounds, pDeletedRange);
}
 
}
 
void ScTokenArray::CheckRelativeReferenceBounds(
    const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        checkBounds(rCxt, rPos, nGroupLen, *p->GetSingleRef(), rBounds);
                    }
                    break;
                case svDoubleRef:
                    {
                        const ScComplexRefData& rRef = *p->GetDoubleRef();
                        checkBounds(rCxt, rPos, nGroupLen, rRef.Ref1, rBounds);
                        checkBounds(rCxt, rPos, nGroupLen, rRef.Ref2, rBounds);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
void ScTokenArray::CheckRelativeReferenceBounds(
    const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const
{
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
        FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svSingleRef:
                    {
                        const ScSingleRefData& rRef = *p->GetSingleRef();
                        checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef, rBounds, nullptr);
                    }
                    break;
                case svDoubleRef:
                    {
                        const ScComplexRefData& rRef = *p->GetDoubleRef();
                        checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref1, rBounds, nullptr);
                        checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref2, rBounds, nullptr);
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
void ScTokenArray::CheckExpandReferenceBounds(
    const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
{
    const SCROW nInsRow = rCxt.maRange.aStart.Row();
    TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
    for (size_t j=0; j<2; ++j)
    {
        FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
        const FormulaToken* const * pEnd = aPtrs.maPointerRange[j].mpStop;
        for (; pp != pEnd; ++pp)
        {
            const FormulaToken* p = aPtrs.getHandledToken(j,pp);
            if (!p)
                continue;
 
            switch (p->GetType())
            {
                case svDoubleRef:
                    {
                        const ScComplexRefData& rRef = *p->GetDoubleRef();
                        bool bStartRowRelative = rRef.Ref1.IsRowRel();
                        bool bEndRowRelative = rRef.Ref2.IsRowRel();
 
                        // For absolute references nothing needs to be done, they stay
                        // the same for all and if to be expanded the group will be
                        // adjusted later.
                        if (!bStartRowRelative && !bEndRowRelative)
                            break;  // switch
 
                        ScRange aAbsStart(rRef.toAbs(*mxSheetLimits, rPos));
                        ScAddress aPos(rPos);
                        aPos.IncRow(nGroupLen);
                        ScRange aAbsEnd(rRef.toAbs(*mxSheetLimits, aPos));
                        // References must be at least two rows to be expandable.
                        if ((aAbsStart.aEnd.Row() - aAbsStart.aStart.Row() < 1) &&
                                (aAbsEnd.aEnd.Row() - aAbsEnd.aStart.Row() < 1))
                            break;  // switch
 
                        // Only need to process if an edge may be touching the
                        // insertion row anywhere within the run of the group.
                        if (!((aAbsStart.aStart.Row() <= nInsRow && nInsRow <= aAbsEnd.aStart.Row()) ||
                                    (aAbsStart.aEnd.Row() <= nInsRow && nInsRow <= aAbsEnd.aEnd.Row())))
                            break;  // switch
 
                        SCROW nStartRow = aAbsStart.aStart.Row();
                        SCROW nEndRow = aAbsStart.aEnd.Row();
                        // Position on first relevant range.
                        SCROW nOffset = 0;
                        if (nEndRow + 1 < nInsRow)
                        {
                            if (bEndRowRelative)
                            {
                                nOffset = nInsRow - nEndRow - 1;
                                nEndRow += nOffset;
                                if (bStartRowRelative)
                                    nStartRow += nOffset;
                            }
                            else    // bStartRowRelative==true
                            {
                                nOffset = nInsRow - nStartRow;
                                nStartRow += nOffset;
                                // Start is overtaking End, swap.
                                bStartRowRelative = false;
                                bEndRowRelative = true;
                            }
                        }
                        for (SCROW i = nOffset; i < nGroupLen; ++i)
                        {
                            bool bSplit = (nStartRow == nInsRow || nEndRow + 1 == nInsRow);
                            if (bSplit)
                                rBounds.push_back( rPos.Row() + i);
 
                            if (bEndRowRelative)
                                ++nEndRow;
                            if (bStartRowRelative)
                            {
                                ++nStartRow;
                                if (!bEndRowRelative && nStartRow == nEndRow)
                                {
                                    // Start is overtaking End, swap.
                                    bStartRowRelative = false;
                                    bEndRowRelative = true;
                                }
                            }
                            if (nInsRow < nStartRow || (!bStartRowRelative && nInsRow <= nEndRow))
                            {
                                if (bSplit && (++i < nGroupLen))
                                    rBounds.push_back( rPos.Row() + i);
                                break;  // for, out of range now
                            }
                        }
                    }
                    break;
                default:
                    ;
            }
        }
    }
}
 
namespace {
 
void appendDouble( const sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, double fVal )
{
    if (rCxt.mxOpCodeMap->isEnglish())
    {
        rtl::math::doubleToUStringBuffer(
            rBuf, fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
    }
    else
    {
        SvtSysLocale aSysLocale;
        rtl::math::doubleToUStringBuffer(
            rBuf, fVal,
            rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
            aSysLocale.GetLocaleData().getNumDecimalSep()[0], true);
    }
}
 
void appendString( OUStringBuffer& rBuf, const OUString& rStr )
{
    rBuf.append('"');
    rBuf.append(rStr.replaceAll("\"", "\"\""));
    rBuf.append('"');
}
 
void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, const FormulaToken& rToken,
        const ScAddress& rPos, bool bFromRangeName )
{
    if (rToken.IsExternalRef())
    {
        size_t nFileId = rToken.GetIndex();
        OUString aTabName = rToken.GetString().getString();
        if (nFileId >= rCxt.maExternalFileNames.size())
            // out of bound
            return;
 
        OUString aFileName = rCxt.maExternalFileNames[nFileId];
 
        switch (rToken.GetType())
        {
            case svExternalName:
                rBuf.append(rCxt.mpRefConv->makeExternalNameStr(nFileId, aFileName, aTabName));
            break;
            case svExternalSingleRef:
                rCxt.mpRefConv->makeExternalRefStr(
                       rLimits, rBuf, rPos, nFileId, aFileName, aTabName, *rToken.GetSingleRef());
            break;
            case svExternalDoubleRef:
            {
                sc::TokenStringContext::IndexNamesMapType::const_iterator it =
                    rCxt.maExternalCachedTabNames.find(nFileId);
 
                if (it == rCxt.maExternalCachedTabNames.end())
                    return;
 
                rCxt.mpRefConv->makeExternalRefStr(
                        rLimits, rBuf, rPos, nFileId, aFileName, it->second, aTabName,
                        *rToken.GetDoubleRef());
            }
            break;
            default:
                // warning, not error, otherwise we may end up with a never
                // ending message box loop if this was the cursor cell to be redrawn.
                OSL_FAIL("appendTokenByType: unknown type of ocExternalRef");
        }
        return;
    }
 
    OpCode eOp = rToken.GetOpCode();
    switch (rToken.GetType())
    {
        case svDouble:
            appendDouble(rCxt, rBuf, rToken.GetDouble());
        break;
        case svString:
        {
            OUString aStr = rToken.GetString().getString();
            if (eOp == ocBad || eOp == ocStringXML || eOp == ocStringName)
            {
                rBuf.append(aStr);
                return;
            }
 
            appendString(rBuf, aStr);
        }
        break;
        case svSingleRef:
        {
            if (rCxt.mpRefConv)
            {
                const ScSingleRefData& rRef = *rToken.GetSingleRef();
                ScComplexRefData aRef;
                aRef.Ref1 = rRef;
                aRef.Ref2 = rRef;
                rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, aRef, true,
                        bFromRangeName);
            }
            else
                rBuf.append(rCxt.maErrRef);
        }
        break;
        case svDoubleRef:
        {
            if (rCxt.mpRefConv)
            {
                const ScComplexRefData& rRef = *rToken.GetDoubleRef();
                rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, rRef, false,
                        bFromRangeName);
            }
            else
                rBuf.append(rCxt.maErrRef);
        }
        break;
        case svMatrix:
        {
            const ScMatrix* pMat = rToken.GetMatrix();
            if (!pMat)
                return;
 
            size_t nC, nMaxC, nR, nMaxR;
            pMat->GetDimensions(nMaxC, nMaxR);
 
            rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayOpen));
            for (nR = 0 ; nR < nMaxR ; ++nR)
            {
                if (nR > 0)
                {
                    rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayRowSep));
                }
 
                for (nC = 0 ; nC < nMaxC ; ++nC)
                {
                    if (nC > 0)
                    {
                        rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayColSep));
                    }
 
                    if (pMat->IsValue(nC, nR))
                    {
                        if (pMat->IsBoolean(nC, nR))
                        {
                            bool bVal = pMat->GetDouble(nC, nR) != 0.0;
                            rBuf.append(rCxt.mxOpCodeMap->getSymbol(bVal ? ocTrue : ocFalse));
                        }
                        else
                        {
                            FormulaError nErr = pMat->GetError(nC, nR);
                            if (nErr != FormulaError::NONE)
                                rBuf.append(ScGlobal::GetErrorString(nErr));
                            else
                                appendDouble(rCxt, rBuf, pMat->GetDouble(nC, nR));
                        }
                    }
                    else if (pMat->IsEmpty(nC, nR))
                    {
                        // Skip it.
                    }
                    else if (pMat->IsStringOrEmpty(nC, nR))
                        appendString(rBuf, pMat->GetString(nC, nR).getString());
                }
            }
            rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayClose));
        }
        break;
        case svIndex:
        {
            typedef sc::TokenStringContext::IndexNameMapType NameType;
 
            sal_uInt16 nIndex = rToken.GetIndex();
            switch (eOp)
            {
                case ocName:
                {
                    SCTAB nTab = rToken.GetSheet();
                    if (nTab < 0)
                    {
                        // global named range
                        NameType::const_iterator it = rCxt.maGlobalRangeNames.find(nIndex);
                        if (it == rCxt.maGlobalRangeNames.end())
                        {
                            rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
                            break;
                        }
 
                        rBuf.append(it->second);
                    }
                    else
                    {
                        // sheet-local named range
                        if (nTab != rPos.Tab())
                        {
                            // On other sheet.
                            OUString aName;
                            if (o3tl::make_unsigned(nTab) < rCxt.maTabNames.size())
                                aName = rCxt.maTabNames[nTab];
                            if (!aName.isEmpty())
                            {
                                ScCompiler::CheckTabQuotes( aName, rCxt.mpRefConv->meConv);
                                rBuf.append( aName);
                            }
                            else
                                rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
                            rBuf.append( rCxt.mpRefConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
                        }
 
                        sc::TokenStringContext::TabIndexMapType::const_iterator itTab = rCxt.maSheetRangeNames.find(nTab);
                        if (itTab == rCxt.maSheetRangeNames.end())
                        {
                            rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
                            break;
                        }
 
                        const NameType& rNames = itTab->second;
                        NameType::const_iterator it = rNames.find(nIndex);
                        if (it == rNames.end())
                        {
                            rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
                            break;
                        }
 
                        rBuf.append(it->second);
                    }
                }
                break;
                case ocDBArea:
                case ocTableRef:
                {
                    NameType::const_iterator it = rCxt.maNamedDBs.find(nIndex);
                    if (it != rCxt.maNamedDBs.end())
                        rBuf.append(it->second);
                }
                break;
                default:
                    rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
            }
        }
        break;
        case svExternal:
        {
            // mapped or translated name of AddIns
            OUString aAddIn = rToken.GetExternal();
            bool bMapped = rCxt.mxOpCodeMap->isPODF();     // ODF 1.1 directly uses programmatical name
            if (!bMapped && rCxt.mxOpCodeMap->hasExternals())
            {
                const ExternalHashMap& rExtMap = rCxt.mxOpCodeMap->getReverseExternalHashMap();
                ExternalHashMap::const_iterator it = rExtMap.find(aAddIn);
                if (it != rExtMap.end())
                {
                    aAddIn = it->second;
                    bMapped = true;
                }
            }
 
            if (!bMapped && !rCxt.mxOpCodeMap->isEnglish())
                ScGlobal::GetAddInCollection()->LocalizeString(aAddIn);
 
            rBuf.append(aAddIn);
        }
        break;
        case svError:
        {
            FormulaError nErr = rToken.GetError();
            OpCode eOpErr;
            switch (nErr)
            {
                case FormulaError::DivisionByZero:
                    eOpErr = ocErrDivZero;
                break;
                case FormulaError::NoValue:
                    eOpErr = ocErrValue;
                break;
                case FormulaError::NoRef:
                    eOpErr = ocErrRef;
                break;
                case FormulaError::NoName:
                    eOpErr = ocErrName;
                break;
                case FormulaError::IllegalFPOperation:
                    eOpErr = ocErrNum;
                break;
                case FormulaError::NotAvailable:
                    eOpErr = ocErrNA;
                break;
                case FormulaError::NoCode:
                default:
                    eOpErr = ocErrNull;
            }
            rBuf.append(rCxt.mxOpCodeMap->getSymbol(eOpErr));
        }
        break;
        case svByte:
        case svJump:
        case svFAP:
        case svMissing:
        case svSep:
        default:
            ;
    }
}
 
}
 
OUString ScTokenArray::CreateString( sc::TokenStringContext& rCxt, const ScAddress& rPos ) const
{
    if (!nLen)
        return OUString();
 
    OUStringBuffer aBuf;
 
    FormulaToken** p = pCode.get();
    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
    for (; p != pEnd; ++p)
    {
        const FormulaToken* pToken = *p;
        OpCode eOp = pToken->GetOpCode();
        /* FIXME: why does this ignore the count of spaces? */
        if (eOp == ocSpaces)
        {
            // TODO : Handle intersection operator '!!'.
            aBuf.append(' ');
            continue;
        }
        else if (eOp == ocWhitespace)
        {
            aBuf.append( pToken->GetChar());
            continue;
        }
 
        if (eOp < rCxt.mxOpCodeMap->getSymbolCount())
            aBuf.append(rCxt.mxOpCodeMap->getSymbol(eOp));
 
        appendTokenByType(*mxSheetLimits, rCxt, aBuf, *pToken, rPos, IsFromRangeName());
    }
 
    return aBuf.makeStringAndClear();
}
 
namespace {
 
void wrapAddress( ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
{
    if (rPos.Col() > nMaxCol)
        rPos.SetCol(rPos.Col() % (nMaxCol+1));
    if (rPos.Row() > nMaxRow)
        rPos.SetRow(rPos.Row() % (nMaxRow+1));
}
 
template<typename T> void wrapRange( T& n1, T& n2, T nMax )
{
    if (n2 > nMax)
    {
        if (n1 == 0)
            n2 = nMax;  // Truncate to full range instead of wrapping to a weird range.
        else
            n2 = n2 % (nMax+1);
    }
    if (n1 > nMax)
        n1 = n1 % (nMax+1);
}
 
void wrapColRange( ScRange& rRange, SCCOL nMaxCol )
{
    SCCOL nCol1 = rRange.aStart.Col();
    SCCOL nCol2 = rRange.aEnd.Col();
    wrapRange( nCol1, nCol2, nMaxCol);
    rRange.aStart.SetCol( nCol1);
    rRange.aEnd.SetCol( nCol2);
}
 
void wrapRowRange( ScRange& rRange, SCROW nMaxRow )
{
    SCROW nRow1 = rRange.aStart.Row();
    SCROW nRow2 = rRange.aEnd.Row();
    wrapRange( nRow1, nRow2, nMaxRow);
    rRange.aStart.SetRow( nRow1);
    rRange.aEnd.SetRow( nRow2);
}
 
}
 
void ScTokenArray::WrapReference( const ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
{
    FormulaToken** p = pCode.get();
    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
    for (; p != pEnd; ++p)
    {
        switch ((*p)->GetType())
        {
            case svSingleRef:
            {
                formula::FormulaToken* pToken = *p;
                ScSingleRefData& rRef = *pToken->GetSingleRef();
                ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
                wrapAddress(aAbs, nMaxCol, nMaxRow);
                rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
            }
            break;
            case svDoubleRef:
            {
                formula::FormulaToken* pToken = *p;
                ScComplexRefData& rRef = *pToken->GetDoubleRef();
                ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
                // Entire columns/rows are sticky.
                if (!rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
                {
                    wrapColRange( aAbs, nMaxCol);
                    wrapRowRange( aAbs, nMaxRow);
                }
                else if (rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
                    wrapColRange( aAbs, nMaxCol);
                else if (!rRef.IsEntireCol(*mxSheetLimits) && rRef.IsEntireRow(*mxSheetLimits))
                    wrapRowRange( aAbs, nMaxRow);
                // else nothing if both, column and row, are entire.
                aAbs.PutInOrder();
                rRef.SetRange(*mxSheetLimits, aAbs, rPos);
            }
            break;
            default:
                ;
        }
    }
}
 
sal_Int32 ScTokenArray::GetWeight() const
{
    sal_Int32 nResult = 0;
    for (auto i = 0; i < nRPN; ++i)
    {
        switch ((*pRPN[i]).GetType())
        {
        case svDoubleRef:
            {
                const auto pComplexRef = (*pRPN[i]).GetDoubleRef();
 
                // Number of cells referenced divided by 10.
                const double nRows = 1 + (pComplexRef->Ref2.Row() - pComplexRef->Ref1.Row());
                const double nCols = 1 + (pComplexRef->Ref2.Col() - pComplexRef->Ref1.Col());
                const double nNumCellsTerm = nRows * nCols / 10.0;
 
                if (nNumCellsTerm + nResult < SAL_MAX_INT32)
                    nResult += nNumCellsTerm;
                else
                    nResult = SAL_MAX_INT32;
            }
            break;
        default:
            ;
        }
    }
 
    if (nResult == 0)
        nResult = 1;
 
    return nResult;
}
 
#if DEBUG_FORMULA_COMPILER
 
void ScTokenArray::Dump() const
{
    cout << "+++ Normal Tokens +++" << endl;
    for (sal_uInt16 i = 0; i < nLen; ++i)
    {
        DumpToken(*pCode[i]);
    }
 
    cout << "+++ RPN Tokens +++" << endl;
    for (sal_uInt16 i = 0; i < nRPN; ++i)
    {
        DumpToken(*pRPN[i]);
    }
}
#endif
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V547 Expression '!"can't move"' is always false.

V547 Expression '!"can't move"' is always false.

V547 Expression '!"can't move"' is always false.

V547 Expression '!"can't move"' is always false.

V1019 Compound assignment expression 'rToken.Data >>= aApiExtRef' is used inside condition.