Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/tool/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 190 kB image not shown  

Quelle  token.cxx   Sprache: C

 
/* -*- 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&&nbsp;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& _rToken1const 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_castconst ScEmptyCellToken & >(r).IsInherited() &&
        bDisplayedAsString == static_castconst ScEmptyCellToken & >(r).IsDisplayedAsString();
}

ScMatrixCellResultToken::ScMatrixCellResultToken( ScConstMatrixRef pMat, const formula::FormulaToken* pUL ) :
    FormulaToken(formula::svMatrixCell), xMatrix(std::move(pMat)), xUpperLeft(pUL) {}

ScMatrixCellResultToken::ScMatrixCellResultToken( const ScMatrixCellResultToken&&nbsp;) = 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&&nbsp;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 ocChooseCols:
            case ocChooseRows:
            case ocDrop:
            case ocExpand:
            case ocHStack:
            case ocVStack:
            case ocTake:
            case ocTextSplit:
            case ocToCol:
            case ocToRow:
            case ocUnique:
            case ocWrapCols:
            case ocWrapRows:
            // 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;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.