Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  interpr1.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 <interpre.hxx>

#include <optional>
#include <scitems.hxx>
#include <editeng/langitem.hxx>
#include <editeng/justifyitem.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/temporary.hxx>
#include <osl/thread.h>
#include <unotools/textsearch.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <tools/urlobj.hxx>
#include <unotools/charclass.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/printer.hxx>
#include <unotools/collatorwrapper.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <rtl/character.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <unicode/uchar.h>
#include <unicode/regex.h>
#include <i18nlangtag/mslangid.hxx>

#include <patattr.hxx>
#include <global.hxx>
#include <document.hxx>
#include <dociter.hxx>
#include <docsh.hxx>
#include <sfx2/linkmgr.hxx>
#include <formulacell.hxx>
#include <scmatrix.hxx>
#include <docoptio.hxx>
#include <attrib.hxx>
#include <jumpmatrix.hxx>
#include <cellkeytranslator.hxx>
#include <lookupcache.hxx>
#include <rangenam.hxx>
#include <rangeutl.hxx>
#include <compiler.hxx>
#include <externalrefmgr.hxx>
#include <doubleref.hxx>
#include <queryparam.hxx>
#include <queryiter.hxx>
#include <tokenarray.hxx>
#include <compare.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <svl/sharedstringpool.hxx>

#include <stdlib.h>
#include <memory>
#include <vector>
#include <limits>
#include <string_view>
#include <cmath>

const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48

ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;

using namespace formula;
using ::std::unique_ptr;

void ScInterpreter::ScIfJump()
{
    const short* pJump = pCur->GetJump();
    short nJumpCount = pJump[ 0 ];
    MatrixJumpConditionToMatrix();
    if ( GetStackType() != svMatrix )
    {
        ScIfJumpNotMatrix(pJump, nJumpCount);
        return;
    }

    ScMatrixRef pMat = PopMatrix();
    if ( !pMat )
    {
        PushIllegalParameter();
        return;
    }

    FormulaConstTokenRef xNew;
    ScTokenMatrixMap::const_iterator aMapIter;
    // DoubleError handled by JumpMatrix
    pMat->SetErrorInterpreter( nullptr);
    SCSIZE nCols, nRows;
    pMat->GetDimensions( nCols, nRows );
    if ( nCols == 0 || nRows == 0 )
    {
        PushIllegalArgument();
        return;
    }

    if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
        xNew = (*aMapIter).second;
    else
    {
        std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
                    pCur->GetOpCode(), nCols, nRows));
        pMat->IfJump(*pJumpMat, pJump, nJumpCount);
        xNew = new ScJumpMatrixToken(std::move(pJumpMat));
        GetTokenMatrixMap().emplace(pCur, xNew);
    }
    if (!xNew)
    {
        PushIllegalArgument();
        return;
    }
    PushTokenRef( xNew);
    // set endpoint of path for main code line
    aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
}

void ScInterpreter::ScIfJumpNotMatrix( const short* pJump, short nJumpCount )
{
    const bool bCondition = GetBool();
    if (nGlobalError != FormulaError::NONE)
    {   // Propagate error, not THEN- or ELSE-path, jump behind.
        PushError(nGlobalError);
        aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
    }
    else if ( bCondition )
    {   // TRUE
        if( nJumpCount >= 2 )
        {   // THEN path
            aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
        }
        else
        {   // no parameter given for THEN
            nFuncFmtType = SvNumFormatType::LOGICAL;
            PushInt(1);
            aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
        }
    }
    else
    {   // FALSE
        if( nJumpCount == 3 )
        {   // ELSE path
            aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
        }
        else
        {   // no parameter given for ELSE
            nFuncFmtType = SvNumFormatType::LOGICAL;
            PushInt(0);
            aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
        }
    }
}


/** Store a matrix value in another matrix in the context of that other matrix
    is the result matrix of a jump matrix. All arguments must be valid and are
    not checked. */

static void lcl_storeJumpMatResult(
    const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
{
    if ( pMat->IsValue( nC, nR ) )
    {
        double fVal = pMat->GetDouble( nC, nR );
        pJumpMat->PutResultDouble( fVal, nC, nR );
    }
    else if ( pMat->IsEmpty( nC, nR ) )
    {
        pJumpMat->PutResultEmpty( nC, nR );
    }
    else
    {
        pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
    }
}

void ScInterpreter::ScIfError( bool bNAonly )
{
    const short* pJump = pCur->GetJump();
    short nJumpCount = pJump[ 0 ];
    if (!sp || nJumpCount != 2)
    {
        // Reset nGlobalError here to not propagate the old error, if any.
        nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
        PushError( nGlobalError);
        aCode.Jump( pJump[ nJumpCount  ], pJump[ nJumpCount ] );
        return;
    }

    FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
    bool bError = false;
    FormulaError nOldGlobalError = nGlobalError;
    nGlobalError = FormulaError::NONE;

    MatrixJumpConditionToMatrix();
    switch (GetStackType())
    {
        default:
            Pop();
            // Act on implicitly propagated error, if any.
            if (nOldGlobalError != FormulaError::NONE)
                nGlobalError = nOldGlobalError;
            if (nGlobalError != FormulaError::NONE)
                bError = true;
            break;
        case svError:
            PopError();
            bError = true;
            break;
        case svDoubleRef:
        case svSingleRef:
            {
                ScAddress aAdr;
                if (!PopDoubleRefOrSingleRef( aAdr))
                    bError = true;
                else
                {

                    ScRefCellValue aCell(mrDoc, aAdr);
                    nGlobalError = GetCellErrCode(aCell);
                    if (nGlobalError != FormulaError::NONE)
                        bError = true;
                }
            }
            break;
        case svExternalSingleRef:
        case svExternalDoubleRef:
        {
            double fVal;
            svl::SharedString aStr;
            // Handles also existing jump matrix case and sets error on
            // elements.
            GetDoubleOrStringFromMatrix( fVal, aStr);
            if (nGlobalError != FormulaError::NONE)
                bError = true;
        }
        break;
        case svMatrix:
            {
                const ScMatrixRef pMat = PopMatrix();
                if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
                {
                    bError = true;
                    break;  // switch
                }
                // If the matrix has no queried error at all we can simply use
                // it as result and don't need to bother with jump matrix.
                SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
                       nErrorRow = ::std::numeric_limits<SCSIZE>::max();
                SCSIZE nCols, nRows;
                pMat->GetDimensions( nCols, nRows );
                if (nCols == 0 || nRows == 0)
                {
                    bError = true;
                    break;  // switch
                }
                for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
                {
                    for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
                    {
                        FormulaError nErr = pMat->GetError( nC, nR );
                        if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
                        {
                            bError = true;
                            nErrorCol = nC;
                            nErrorRow = nR;
                        }
                    }
                }
                if (!bError)
                    break;  // switch, we're done and have the result

                FormulaConstTokenRef xNew;
                ScTokenMatrixMap::const_iterator aMapIter;
                if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
                {
                    xNew = (*aMapIter).second;
                }
                else
                {
                    const ScMatrix* pMatPtr = pMat.get();
                    std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
                                pCur->GetOpCode(), nCols, nRows));
                    // Init all jumps to no error to save single calls. Error
                    // is the exceptional condition.
                    const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
                    pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
                    // Up to first error position simply store results, no need
                    // to evaluate error conditions again.
                    SCSIZE nC = 0, nR = 0;
                    for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
                    {
                        for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
                        {
                            lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
                        }
                        if (nC != nErrorCol && nR != nErrorRow)
                            ++nC;
                    }
                    // Now the mixed cases.
                    for ( ; nC < nCols; ++nC)
                    {
                        for ( ; nR < nRows; ++nR)
                        {
                            FormulaError nErr = pMat->GetError( nC, nR );
                            if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
                            {   // TRUE, THEN path
                                pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
                            }
                            else
                            {   // FALSE, EMPTY path, store result instead
                                lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
                            }
                        }
                        nR = 0;
                    }
                    xNew = new ScJumpMatrixToken(std::move(pJumpMat));
                    GetTokenMatrixMap().emplace( pCur, xNew );
                }
                nGlobalError = nOldGlobalError;
                PushTokenRef( xNew );
                // set endpoint of path for main code line
                aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
                return;
            }
            break;
    }

    if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
    {
        // error, calculate 2nd argument
        nGlobalError = FormulaError::NONE;
        aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
    }
    else
    {
        // no error, push 1st argument and continue
        nGlobalError = nOldGlobalError;
        PushTokenRef( xToken);
        aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
    }
}

void ScInterpreter::ScChooseJump()
{
    // We have to set a jump, if there was none chosen because of an error set
    // it to endpoint.
    bool bHaveJump = false;
    const short* pJump = pCur->GetJump();
    short nJumpCount = pJump[ 0 ];
    MatrixJumpConditionToMatrix();
    switch ( GetStackType() )
    {
        case svMatrix:
        {
            ScMatrixRef pMat = PopMatrix();
            if ( !pMat )
                PushIllegalParameter();
            else
            {
                FormulaConstTokenRef xNew;
                ScTokenMatrixMap::const_iterator aMapIter;
                // DoubleError handled by JumpMatrix
                pMat->SetErrorInterpreter( nullptr);
                SCSIZE nCols, nRows;
                pMat->GetDimensions( nCols, nRows );
                if ( nCols == 0 || nRows == 0 )
                    PushIllegalParameter();
                else if ((aMapIter = maTokenMatrixMap.find(
                                    pCur)) != maTokenMatrixMap.end())
                    xNew = (*aMapIter).second;
                else
                {
                    std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
                                pCur->GetOpCode(), nCols, nRows));
                    for ( SCSIZE nC=0; nC < nCols; ++nC )
                    {
                        for ( SCSIZE nR=0; nR < nRows; ++nR )
                        {
                            double fVal;
                            bool bIsValue = pMat->IsValue(nC, nR);
                            if ( bIsValue )
                            {
                                fVal = pMat->GetDouble(nC, nR);
                                bIsValue = std::isfinite( fVal );
                                if ( bIsValue )
                                {
                                    fVal = ::rtl::math::approxFloor( fVal);
                                    if ( (fVal < 1) || (fVal >= nJumpCount))
                                    {
                                        bIsValue = false;
                                        fVal = CreateDoubleError(
                                                FormulaError::IllegalArgument);
                                    }
                                }
                            }
                            else
                            {
                                fVal = CreateDoubleError( FormulaError::NoValue);
                            }
                            if ( bIsValue )
                            {
                                pJumpMat->SetJump( nC, nR, fVal,
                                        pJump[ static_cast<short>(fVal) ],
                                        pJump[ nJumpCount ]);
                            }
                            else
                            {
                                pJumpMat->SetJump( nC, nR, fVal,
                                        pJump[ nJumpCount ],
                                        pJump[ nJumpCount ]);
                            }
                        }
                    }
                    xNew = new ScJumpMatrixToken(std::move(pJumpMat));
                    GetTokenMatrixMap().emplace(pCur, xNew);
                }
                if (xNew)
                {
                    PushTokenRef( xNew);
                    // set endpoint of path for main code line
                    aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
                    bHaveJump = true;
                }
            }
        }
        break;
        default:
        {
            sal_Int16 nJumpIndex = GetInt16();
            if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
            {
                aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
                bHaveJump = true;
            }
            else
                PushIllegalArgument();
        }
    }
    if (!bHaveJump)
        aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
}

static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
{
    SCSIZE nJumpCols, nJumpRows;
    SCSIZE nResCols, nResRows;
    SCSIZE nAdjustCols, nAdjustRows;
    pJumpM->GetDimensions( nJumpCols, nJumpRows );
    pJumpM->GetResMatDimensions( nResCols, nResRows );
    if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
        ( nJumpRows == 1 && nParmRows > nResRows )))
        return;

    if ( nJumpCols == 1 && nJumpRows == 1 )
    {
        nAdjustCols = std::max(nParmCols, nResCols);
        nAdjustRows = std::max(nParmRows, nResRows);
    }
    else if ( nJumpCols == 1 )
    {
        nAdjustCols = nParmCols;
        nAdjustRows = nResRows;
    }
    else
    {
        nAdjustCols = nResCols;
        nAdjustRows = nParmRows;
    }
    pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
}

bool ScInterpreter::JumpMatrix( short nStackLevel )
{
    pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
    bool bHasResMat = pJumpMatrix->HasResultMatrix();
    SCSIZE nC, nR;
    if ( nStackLevel == 2 )
    {
        if ( aCode.HasStacked() )
            aCode.Pop();    // pop what Jump() pushed
        else
        {
            assert(!"pop goes the weasel");
        }

        if ( !bHasResMat )
        {
            Pop();
            SetError( FormulaError::UnknownStackVariable );
        }
        else
        {
            pJumpMatrix->GetPos( nC, nR );
            switch ( GetStackType() )
            {
                case svDouble:
                {
                    double fVal = GetDouble();
                    if ( nGlobalError != FormulaError::NONE )
                    {
                        fVal = CreateDoubleError( nGlobalError );
                        nGlobalError = FormulaError::NONE;
                    }
                    pJumpMatrix->PutResultDouble( fVal, nC, nR );
                }
                break;
                case svString:
                {
                    svl::SharedString aStr = GetString();
                    if ( nGlobalError != FormulaError::NONE )
                    {
                        pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
                                nC, nR);
                        nGlobalError = FormulaError::NONE;
                    }
                    else
                        pJumpMatrix->PutResultString(aStr, nC, nR);
                }
                break;
                case svSingleRef:
                {
                    FormulaConstTokenRef xRef = pStack[sp-1];
                    ScAddress aAdr;
                    PopSingleRef( aAdr );
                    if ( nGlobalError != FormulaError::NONE )
                    {
                        pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
                                nC, nR);
                        nGlobalError = FormulaError::NONE;
                    }
                    else
                    {
                        ScRefCellValue aCell(mrDoc, aAdr);
                        if (aCell.hasEmptyValue())
                            pJumpMatrix->PutResultEmpty( nC, nR );
                        else if (aCell.hasNumeric())
                        {
                            double fVal = GetCellValue(aAdr, aCell);
                            if ( nGlobalError != FormulaError::NONE )
                            {
                                fVal = CreateDoubleError(
                                        nGlobalError);
                                nGlobalError = FormulaError::NONE;
                            }
                            pJumpMatrix->PutResultDouble( fVal, nC, nR );
                        }
                        else
                        {
                            svl::SharedString aStr;
                            GetCellString(aStr, aCell);
                            if ( nGlobalError != FormulaError::NONE )
                            {
                                pJumpMatrix->PutResultDouble( CreateDoubleError(
                                            nGlobalError), nC, nR);
                                nGlobalError = FormulaError::NONE;
                            }
                            else
                                pJumpMatrix->PutResultString(aStr, nC, nR);
                        }
                    }

                    formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
                    if (eReturnType == ParamClass::Reference)
                    {
                        /* TODO: What about error handling and do we actually
                         * need the result matrix above at all in this case? */

                        ScComplexRefData aRef;
                        aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
                        pJumpMatrix->GetRefList().push_back( aRef);
                    }
                }
                break;
                case svDoubleRef:
                {   // upper left plus offset within matrix
                    FormulaConstTokenRef xRef = pStack[sp-1];
                    double fVal;
                    ScRange aRange;
                    PopDoubleRef( aRange );
                    if ( nGlobalError != FormulaError::NONE )
                    {
                        fVal = CreateDoubleError( nGlobalError );
                        nGlobalError = FormulaError::NONE;
                        pJumpMatrix->PutResultDouble( fVal, nC, nR );
                    }
                    else
                    {
                        // Do not modify the original range because we use it
                        // to adjust the size of the result matrix if necessary.
                        ScAddress aAdr( aRange.aStart);
                        sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
                        sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
                        if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
                                    aRange.aEnd.Col() != aRange.aStart.Col())
                                || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
                                    aRange.aEnd.Row() != aRange.aStart.Row()))
                        {
                            fVal = CreateDoubleError( FormulaError::NotAvailable );
                            pJumpMatrix->PutResultDouble( fVal, nC, nR );
                        }
                        else
                        {
                            // Replicate column and/or row of a vector if it is
                            // one. Note that this could be a range reference
                            // that in fact consists of only one cell, e.g. A1:A1
                            if (aRange.aEnd.Col() == aRange.aStart.Col())
                                nCol = aRange.aStart.Col();
                            if (aRange.aEnd.Row() == aRange.aStart.Row())
                                nRow = aRange.aStart.Row();
                            aAdr.SetCol( static_cast<SCCOL>(nCol) );
                            aAdr.SetRow( static_cast<SCROW>(nRow) );
                            ScRefCellValue aCell(mrDoc, aAdr);
                            if (aCell.hasEmptyValue())
                                pJumpMatrix->PutResultEmpty( nC, nR );
                            else if (aCell.hasNumeric())
                            {
                                double fCellVal = GetCellValue(aAdr, aCell);
                                if ( nGlobalError != FormulaError::NONE )
                                {
                                    fCellVal = CreateDoubleError(
                                            nGlobalError);
                                    nGlobalError = FormulaError::NONE;
                                }
                                pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
                            }
                            else
                            {
                                svl::SharedString aStr;
                                GetCellString(aStr, aCell);
                                if ( nGlobalError != FormulaError::NONE )
                                {
                                    pJumpMatrix->PutResultDouble( CreateDoubleError(
                                                nGlobalError), nC, nR);
                                    nGlobalError = FormulaError::NONE;
                                }
                                else
                                    pJumpMatrix->PutResultString(aStr, nC, nR);
                            }
                        }
                        SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
                        SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
                        lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
                    }

                    formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
                    if (eReturnType == ParamClass::Reference)
                    {
                        /* TODO: What about error handling and do we actually
                         * need the result matrix above at all in this case? */

                        pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
                    }
                }
                break;
                case svExternalSingleRef:
                {
                    ScExternalRefCache::TokenRef pToken;
                    PopExternalSingleRef(pToken);
                    if (nGlobalError != FormulaError::NONE)
                    {
                        pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
                        nGlobalError = FormulaError::NONE;
                    }
                    else
                    {
                        switch (pToken->GetType())
                        {
                            case svDouble:
                                pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
                            break;
                            case svString:
                                pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
                            break;
                            case svEmptyCell:
                                pJumpMatrix->PutResultEmpty( nC, nR );
                            break;
                            default:
                                // svError was already handled (set by
                                // PopExternalSingleRef()) with nGlobalError
                                // above.
                                assert(!"unhandled svExternalSingleRef case");
                                pJumpMatrix->PutResultDouble( CreateDoubleError(
                                            FormulaError::UnknownStackVariable), nC, nR );
                        }
                    }
                }
                break;
                case svExternalDoubleRef:
                case svMatrix:
                {   // match matrix offsets
                    double fVal;
                    ScMatrixRef pMat = GetMatrix();
                    if ( nGlobalError != FormulaError::NONE )
                    {
                        fVal = CreateDoubleError( nGlobalError );
                        nGlobalError = FormulaError::NONE;
                        pJumpMatrix->PutResultDouble( fVal, nC, nR );
                    }
                    else if ( !pMat )
                    {
                        fVal = CreateDoubleError( FormulaError::UnknownVariable );
                        pJumpMatrix->PutResultDouble( fVal, nC, nR );
                    }
                    else
                    {
                        SCSIZE nCols, nRows;
                        pMat->GetDimensions( nCols, nRows );
                        if ((nCols <= nC && nCols != 1) ||
                            (nRows <= nR && nRows != 1))
                        {
                            fVal = CreateDoubleError( FormulaError::NotAvailable );
                            pJumpMatrix->PutResultDouble( fVal, nC, nR );
                        }
                        else
                        {
                            // GetMatrix() does SetErrorInterpreter() at the
                            // matrix, do not propagate an error from
                            // matrix->GetValue() as global error.
                            pMat->SetErrorInterpreter(nullptr);
                            lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
                        }
                        lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
                    }
                }
                break;
                case svError:
                {
                    PopError();
                    double fVal = CreateDoubleError( nGlobalError);
                    nGlobalError = FormulaError::NONE;
                    pJumpMatrix->PutResultDouble( fVal, nC, nR );
                }
                break;
                default:
                {
                    Pop();
                    double fVal = CreateDoubleError( FormulaError::IllegalArgument);
                    pJumpMatrix->PutResultDouble( fVal, nC, nR );
                }
            }
        }
    }
    bool bCont = pJumpMatrix->Next( nC, nR );
    if ( bCont )
    {
        double fBool;
        short nStart, nNext, nStop;
        pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
        while ( bCont && nStart == nNext )
        {   // push all results that have no jump path
            if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
            {
                // a false without path results in an empty path value
                if ( fBool == 0.0 )
                    pJumpMatrix->PutResultEmptyPath( nC, nR );
                else
                    pJumpMatrix->PutResultDouble( fBool, nC, nR );
            }
            bCont = pJumpMatrix->Next( nC, nR );
            if ( bCont )
                pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
        }
        if ( bCont && nStart != nNext )
        {
            const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
            for ( auto const & i : rParams )
            {
                // This is not the current state of the interpreter, so
                // push without error, and elements' errors are coded into
                // double.
                PushWithoutError(*i);
            }
            aCode.Jump( nStart, nNext, nStop );
        }
    }
    if ( !bCont )
    {   // We're done with it, throw away jump matrix, keep result.
        // For an intermediate result of Reference use the array of references
        // if there are more than one reference and the current ForceArray
        // context is ReferenceOrRefArray.
        // Else (also for a final result of Reference) use the matrix.
        // Treat the result of a jump command as final and use the matrix (see
        // tdf#115493 for why).
        if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
                pJumpMatrix->GetRefList().size() > 1 &&
                ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
                !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
                aCode.PeekNextOperator())
        {
            FormulaTokenRef xRef = new ScRefListToken(true);
            *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
            pJumpMatrix = nullptr;
            Pop();
            PushTokenRef( xRef);
            maTokenMatrixMap.erase( pCur);
            // There's no result matrix to remember in this case.
        }
        else
        {
            ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
            pJumpMatrix = nullptr;
            Pop();
            PushMatrix( pResMat );
            // Remove jump matrix from map and remember result matrix in case it
            // could be reused in another path of the same condition.
            maTokenMatrixMap.erase( pCur);
            maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
        }
        return true;
    }
    return false;
}

double ScInterpreter::Compare( ScQueryOp eOp )
{
    sc::Compare aComp;
    aComp.meOp = eOp;
    aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
    forshort i = 1; i >= 0; i-- )
    {
        sc::Compare::Cell& rCell = aComp.maCells[i];

        switch ( GetRawStackType() )
        {
            case svEmptyCell:
                Pop();
                rCell.mbEmpty = true;
                break;
            case svMissing:
            case svDouble:
                rCell.mfValue = GetDouble();
                rCell.mbValue = true;
                break;
            case svString:
                rCell.maStr = GetString();
                rCell.mbValue = false;
                break;
            case svDoubleRef :
            case svSingleRef :
            {
                ScAddress aAdr;
                if ( !PopDoubleRefOrSingleRef( aAdr ) )
                    break;
                ScRefCellValue aCell(mrDoc, aAdr);
                if (aCell.hasEmptyValue())
                    rCell.mbEmpty = true;
                else if (aCell.hasString())
                {
                    svl::SharedString aStr;
                    GetCellString(aStr, aCell);
                    rCell.maStr = std::move(aStr);
                    rCell.mbValue = false;
                }
                else
                {
                    rCell.mfValue = GetCellValue(aAdr, aCell);
                    rCell.mbValue = true;
                }
            }
            break;
            case svExternalSingleRef:
            {
                ScMatrixRef pMat = GetMatrix();
                if (!pMat)
                {
                    SetError( FormulaError::IllegalParameter);
                    break;
                }

                SCSIZE nC, nR;
                pMat->GetDimensions(nC, nR);
                if (!nC || !nR)
                {
                    SetError( FormulaError::IllegalParameter);
                    break;
                }
                if (pMat->IsEmpty(0, 0))
                    rCell.mbEmpty = true;
                else if (pMat->IsStringOrEmpty(0, 0))
                {
                    rCell.maStr = pMat->GetString(0, 0);
                    rCell.mbValue = false;
                }
                else
                {
                    rCell.mfValue = pMat->GetDouble(0, 0);
                    rCell.mbValue = true;
                }
            }
            break;
            case svExternalDoubleRef:
                // TODO: Find out how to handle this...
                // Xcl generates a position dependent intersection using
                // col/row, as it seems to do for all range references, not
                // only in compare context. We'd need a general implementation
                // for that behavior similar to svDoubleRef in scalar and array
                // mode. Which also means we'd have to change all places where
                // it currently is handled along with svMatrix.
            default:
                PopError();
                SetError( FormulaError::IllegalParameter);
            break;
        }
    }
    if( nGlobalError != FormulaError::NONE )
        return 0;
    nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
    return sc::CompareFunc(aComp);
}

sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
{
    sc::Compare aComp;
    aComp.meOp = eOp;
    aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
    sc::RangeMatrix aMat[2];
    ScAddress aAdr;
    forshort i = 1; i >= 0; i-- )
    {
        sc::Compare::Cell& rCell = aComp.maCells[i];

        switch (GetRawStackType())
        {
            case svEmptyCell:
                Pop();
                rCell.mbEmpty = true;
                break;
            case svMissing:
            case svDouble:
                rCell.mfValue = GetDouble();
                rCell.mbValue = true;
                break;
            case svString:
                rCell.maStr = GetString();
                rCell.mbValue = false;
                break;
            case svSingleRef:
            {
                PopSingleRef( aAdr );
                ScRefCellValue aCell(mrDoc, aAdr);
                if (aCell.hasEmptyValue())
                    rCell.mbEmpty = true;
                else if (aCell.hasString())
                {
                    svl::SharedString aStr;
                    GetCellString(aStr, aCell);
                    rCell.maStr = std::move(aStr);
                    rCell.mbValue = false;
                }
                else
                {
                    rCell.mfValue = GetCellValue(aAdr, aCell);
                    rCell.mbValue = true;
                }
            }
            break;
            case svExternalSingleRef:
            case svExternalDoubleRef:
            case svDoubleRef:
            case svMatrix:
                aMat[i] = GetRangeMatrix();
                if (!aMat[i].mpMat)
                    SetError( FormulaError::IllegalParameter);
                else
                    aMat[i].mpMat->SetErrorInterpreter(nullptr);
                    // errors are transported as DoubleError inside matrix
                break;
            default:
                PopError();
                SetError( FormulaError::IllegalParameter);
            break;
        }
    }

    sc::RangeMatrix aRes;

    if (nGlobalError != FormulaError::NONE)
    {
        nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
        return aRes;
    }

    if (aMat[0].mpMat && aMat[1].mpMat)
    {
        SCSIZE nC0, nC1;
        SCSIZE nR0, nR1;
        aMat[0].mpMat->GetDimensions(nC0, nR0);
        aMat[1].mpMat->GetDimensions(nC1, nR1);
        SCSIZE nC = std::max( nC0, nC1 );
        SCSIZE nR = std::max( nR0, nR1 );
        aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true );
        if (!aRes.mpMat)
            return aRes;
        for ( SCSIZE j=0; j<nC; j++ )
        {
            for ( SCSIZE k=0; k<nR; k++ )
            {
                SCSIZE nCol = j, nRow = k;
                if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
                    aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
                {
                    for ( short i=1; i>=0; i-- )
                    {
                        sc::Compare::Cell& rCell = aComp.maCells[i];

                        if (aMat[i].mpMat->IsStringOrEmpty(j, k))
                        {
                            rCell.mbValue = false;
                            rCell.maStr = aMat[i].mpMat->GetString(j, k);
                            rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
                        }
                        else
                        {
                            rCell.mbValue = true;
                            rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
                            rCell.mbEmpty = false;
                        }
                    }
                    aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
                }
                else
                    aRes.mpMat->PutError( FormulaError::NoValue, j, k);
            }
        }

        switch (eOp)
        {
            case SC_EQUAL:
                aRes.mpMat->CompareEqual();
                break;
            case SC_LESS:
                aRes.mpMat->CompareLess();
                break;
            case SC_GREATER:
                aRes.mpMat->CompareGreater();
                break;
            case SC_LESS_EQUAL:
                aRes.mpMat->CompareLessEqual();
                break;
            case SC_GREATER_EQUAL:
                aRes.mpMat->CompareGreaterEqual();
                break;
            case SC_NOT_EQUAL:
                aRes.mpMat->CompareNotEqual();
                break;
            default:
                SAL_WARN("sc",  "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
                aRes.mpMat.reset();
                return aRes;
        }
    }
    else if (aMat[0].mpMat || aMat[1].mpMat)
    {
        size_t i = ( aMat[0].mpMat ? 0 : 1);

        aRes.mnCol1 = aMat[i].mnCol1;
        aRes.mnRow1 = aMat[i].mnRow1;
        aRes.mnTab1 = aMat[i].mnTab1;
        aRes.mnCol2 = aMat[i].mnCol2;
        aRes.mnRow2 = aMat[i].mnRow2;
        aRes.mnTab2 = aMat[i].mnTab2;

        ScMatrix& rMat = *aMat[i].mpMat;
        aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
        if (!aRes.mpMat)
            return aRes;
    }

    nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
    return aRes;
}

ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions&&nbsp;rOptions )
{
    SvNumFormatType nSaveCurFmtType = nCurFmtType;
    SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
    PushMatrix( pMat);
    const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
    if (rItem.meType == ScQueryEntry::ByString)
        PushString(rItem.maString.getString());
    else
        PushDouble(rItem.mfVal);
    ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
    nCurFmtType = nSaveCurFmtType;
    nFuncFmtType = nSaveFuncFmtType;
    if (nGlobalError != FormulaError::NONE || !pResultMatrix)
    {
        SetError( FormulaError::IllegalParameter);
        return pResultMatrix;
    }

    return pResultMatrix;
}

void ScInterpreter::ScEqual()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_EQUAL) == 0) );
}

void ScInterpreter::ScNotEqual()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
}

void ScInterpreter::ScLess()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_LESS);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_LESS) < 0) );
}

void ScInterpreter::ScGreater()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_GREATER);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_GREATER) > 0) );
}

void ScInterpreter::ScLessEqual()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
}

void ScInterpreter::ScGreaterEqual()
{
    if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
    {
        sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
        if (!aMat.mpMat)
        {
            PushIllegalParameter();
            return;
        }

        PushMatrix(aMat);
    }
    else
        PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
}

void ScInterpreter::ScAnd()
{
    nFuncFmtType = SvNumFormatType::LOGICAL;
    short nParamCount = GetByte();
    if ( !MustHaveParamCountMin( nParamCount, 1 ) )
        return;

    bool bHaveValue = false;
    bool bRes = true;
    size_t nRefInList = 0;
    while( nParamCount-- > 0)
    {
        if ( nGlobalError == FormulaError::NONE )
        {
            switch ( GetStackType() )
            {
                case svDouble :
                    bHaveValue = true;
                    bRes &= ( PopDouble() != 0.0 );
                break;
                case svString :
                    Pop();
                    SetError( FormulaError::NoValue );
                break;
                case svSingleRef :
                {
                    ScAddress aAdr;
                    PopSingleRef( aAdr );
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        ScRefCellValue aCell(mrDoc, aAdr);
                        if (aCell.hasNumeric())
                        {
                            bHaveValue = true;
                            bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
                        }
                        // else: Xcl raises no error here
                    }
                }
                break;
                case svDoubleRef:
                case svRefList:
                {
                    ScRange aRange;
                    PopDoubleRef( aRange, nParamCount, nRefInList);
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        double fVal;
                        FormulaError nErr = FormulaError::NONE;
                        ScValueIterator aValIter( mrContext, aRange );
                        if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
                        {
                            bHaveValue = true;
                            do
                            {
                                bRes &= ( fVal != 0.0 );
                            } while ( (nErr == FormulaError::NONE) &&
                                aValIter.GetNext( fVal, nErr ) );
                        }
                        SetError( nErr );
                    }
                }
                break;
                case svExternalSingleRef:
                case svExternalDoubleRef:
                case svMatrix:
                {
                    ScMatrixRef pMat = GetMatrix();
                    if ( pMat )
                    {
                        bHaveValue = true;
                        double fVal = pMat->And();
                        FormulaError nErr = GetDoubleErrorValue( fVal );
                        if ( nErr != FormulaError::NONE )
                        {
                            SetError( nErr );
                            bRes = false;
                        }
                        else
                            bRes &= (fVal != 0.0);
                    }
                    // else: GetMatrix did set FormulaError::IllegalParameter
                }
                break;
                default:
                    PopError();
                    SetError( FormulaError::IllegalParameter);
            }
        }
        else
            Pop();
    }
    if ( bHaveValue )
        PushInt( int(bRes) );
    else
        PushNoValue();
}

void ScInterpreter::ScOr()
{
    nFuncFmtType = SvNumFormatType::LOGICAL;
    short nParamCount = GetByte();
    if ( !MustHaveParamCountMin( nParamCount, 1 ) )
        return;

    bool bHaveValue = false;
    bool bRes = false;
    size_t nRefInList = 0;
    while( nParamCount-- > 0)
    {
        if ( nGlobalError == FormulaError::NONE )
        {
            switch ( GetStackType() )
            {
                case svDouble :
                    bHaveValue = true;
                    bRes |= ( PopDouble() != 0.0 );
                break;
                case svString :
                    Pop();
                    SetError( FormulaError::NoValue );
                break;
                case svSingleRef :
                {
                    ScAddress aAdr;
                    PopSingleRef( aAdr );
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        ScRefCellValue aCell(mrDoc, aAdr);
                        if (aCell.hasNumeric())
                        {
                            bHaveValue = true;
                            bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
                        }
                        // else: Xcl raises no error here
                    }
                }
                break;
                case svDoubleRef:
                case svRefList:
                {
                    ScRange aRange;
                    PopDoubleRef( aRange, nParamCount, nRefInList);
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        double fVal;
                        FormulaError nErr = FormulaError::NONE;
                        ScValueIterator aValIter( mrContext, aRange );
                        if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
                        {
                            bHaveValue = true;
                            do
                            {
                                bRes |= ( fVal != 0.0 );
                            } while ( (nErr == FormulaError::NONE) &&
                                aValIter.GetNext( fVal, nErr ) );
                        }
                        SetError( nErr );
                    }
                }
                break;
                case svExternalSingleRef:
                case svExternalDoubleRef:
                case svMatrix:
                {
                    bHaveValue = true;
                    ScMatrixRef pMat = GetMatrix();
                    if ( pMat )
                    {
                        bHaveValue = true;
                        double fVal = pMat->Or();
                        FormulaError nErr = GetDoubleErrorValue( fVal );
                        if ( nErr != FormulaError::NONE )
                        {
                            SetError( nErr );
                            bRes = false;
                        }
                        else
                            bRes |= (fVal != 0.0);
                    }
                    // else: GetMatrix did set FormulaError::IllegalParameter
                }
                break;
                default:
                    PopError();
                    SetError( FormulaError::IllegalParameter);
            }
        }
        else
            Pop();
    }
    if ( bHaveValue )
        PushInt( int(bRes) );
    else
        PushNoValue();
}

void ScInterpreter::ScXor()
{

    nFuncFmtType = SvNumFormatType::LOGICAL;
    short nParamCount = GetByte();
    if ( !MustHaveParamCountMin( nParamCount, 1 ) )
        return;

    bool bHaveValue = false;
    bool bRes = false;
    size_t nRefInList = 0;
    while( nParamCount-- > 0)
    {
        if ( nGlobalError == FormulaError::NONE )
        {
            switch ( GetStackType() )
            {
                case svDouble :
                    bHaveValue = true;
                    bRes ^= ( PopDouble() != 0.0 );
                break;
                case svString :
                    Pop();
                    SetError( FormulaError::NoValue );
                break;
                case svSingleRef :
                {
                    ScAddress aAdr;
                    PopSingleRef( aAdr );
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        ScRefCellValue aCell(mrDoc, aAdr);
                        if (aCell.hasNumeric())
                        {
                            bHaveValue = true;
                            bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
                        }
                        /* TODO: set error? Excel doesn't have XOR, but
                         * doesn't set an error in this case for AND and
                         * OR. */

                    }
                }
                break;
                case svDoubleRef:
                case svRefList:
                {
                    ScRange aRange;
                    PopDoubleRef( aRange, nParamCount, nRefInList);
                    if ( nGlobalError == FormulaError::NONE )
                    {
                        double fVal;
                        FormulaError nErr = FormulaError::NONE;
                        ScValueIterator aValIter( mrContext, aRange );
                        if ( aValIter.GetFirst( fVal, nErr ) )
                        {
                            bHaveValue = true;
                            do
                            {
                                bRes ^= ( fVal != 0.0 );
                            } while ( (nErr == FormulaError::NONE) &&
                                aValIter.GetNext( fVal, nErr ) );
                        }
                        SetError( nErr );
                    }
                }
                break;
                case svExternalSingleRef:
                case svExternalDoubleRef:
                case svMatrix:
                {
                    bHaveValue = true;
                    ScMatrixRef pMat = GetMatrix();
                    if ( pMat )
                    {
                        bHaveValue = true;
                        double fVal = pMat->Xor();
                        FormulaError nErr = GetDoubleErrorValue( fVal );
                        if ( nErr != FormulaError::NONE )
                        {
                            SetError( nErr );
                            bRes = false;
                        }
                        else
                            bRes ^= ( fVal != 0.0 );
                    }
                    // else: GetMatrix did set FormulaError::IllegalParameter
                }
                break;
                default:
                    PopError();
                    SetError( FormulaError::IllegalParameter);
            }
        }
        else
            Pop();
    }
    if ( bHaveValue )
        PushInt( int(bRes) );
    else
        PushNoValue();
}

void ScInterpreter::ScNeg()
{
    // Simple negation doesn't change current format type to number, keep
    // current type.
    nFuncFmtType = nCurFmtType;
    switch ( GetStackType() )
    {
        case svMatrix :
        {
            ScMatrixRef pMat = GetMatrix();
            if ( !pMat )
                PushIllegalParameter();
            else
            {
                SCSIZE nC, nR;
                pMat->GetDimensions( nC, nR );
                ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true );
                if ( !pResMat )
                    PushIllegalArgument();
                else
                {
                    pMat->NegOp( *pResMat);
                    PushMatrix( pResMat );
                }
            }
        }
        break;
        default:
            PushDouble( -GetDouble() );
    }
}

void ScInterpreter::ScPercentSign()
{
    nFuncFmtType = SvNumFormatType::PERCENT;
    const FormulaToken* pSaveCur = pCur;
    sal_uInt8 nSavePar = cPar;
    PushInt( 100 );
    cPar = 2;
    FormulaByteToken aDivOp( ocDiv, cPar );
    pCur = &aDivOp;
    ScDiv();
    pCur = pSaveCur;
    cPar = nSavePar;
}

void ScInterpreter::ScNot()
{
    nFuncFmtType = SvNumFormatType::LOGICAL;
    switch ( GetStackType() )
    {
        case svMatrix :
        {
            ScMatrixRef pMat = GetMatrix();
            if ( !pMat )
                PushIllegalParameter();
            else
            {
                SCSIZE nC, nR;
                pMat->GetDimensions( nC, nR );
                ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true);
                if ( !pResMat )
                    PushIllegalArgument();
                else
                {
                    pMat->NotOp( *pResMat);
                    PushMatrix( pResMat );
                }
            }
        }
        break;
        default:
            PushInt( int(GetDouble() == 0.0) );
    }
}

void ScInterpreter::ScBitAnd()
{

    if ( !MustHaveParamCount( GetByte(), 2 ) )
        return;

    double num1 = ::rtl::math::approxFloor( GetDouble());
    double num2 = ::rtl::math::approxFloor( GetDouble());
    if (    (num1 >= n2power48) || (num1 < 0) ||
            (num2 >= n2power48) || (num2 < 0))
        PushIllegalArgument();
    else
        PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
}

void ScInterpreter::ScBitOr()
{

    if ( !MustHaveParamCount( GetByte(), 2 ) )
        return;

    double num1 = ::rtl::math::approxFloor( GetDouble());
    double num2 = ::rtl::math::approxFloor( GetDouble());
    if (    (num1 >= n2power48) || (num1 < 0) ||
            (num2 >= n2power48) || (num2 < 0))
        PushIllegalArgument();
    else
        PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
}

void ScInterpreter::ScBitXor()
{

    if ( !MustHaveParamCount( GetByte(), 2 ) )
        return;

    double num1 = ::rtl::math::approxFloor( GetDouble());
    double num2 = ::rtl::math::approxFloor( GetDouble());
    if (    (num1 >= n2power48) || (num1 < 0) ||
            (num2 >= n2power48) || (num2 < 0))
        PushIllegalArgument();
    else
        PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
}

void ScInterpreter::ScBitLshift()
{

    if ( !MustHaveParamCount( GetByte(), 2 ) )
        return;

    double fShift = ::rtl::math::approxFloor( GetDouble());
    double num = ::rtl::math::approxFloor( GetDouble());
    if ((num >= n2power48) || (num < 0))
        PushIllegalArgument();
    else
    {
        double fRes;
        if (fShift < 0)
            fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
        else if (fShift == 0)
            fRes = num;
        else
            fRes = num * pow( 2.0, fShift);
        PushDouble( fRes);
    }
}

void ScInterpreter::ScBitRshift()
{

    if ( !MustHaveParamCount( GetByte(), 2 ) )
        return;

    double fShift = ::rtl::math::approxFloor( GetDouble());
    double num = ::rtl::math::approxFloor( GetDouble());
    if ((num >= n2power48) || (num < 0))
        PushIllegalArgument();
    else
    {
        double fRes;
        if (fShift < 0)
            fRes = num * pow( 2.0, -fShift);
        else if (fShift == 0)
            fRes = num;
        else
            fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
        PushDouble( fRes);
    }
}

void ScInterpreter::ScPi()
{
    PushDouble(M_PI);
}

void ScInterpreter::ScRandomImpl( const std::function<doubledouble fFirst, double fLast )>& RandomFunc,
        double fFirst, double fLast )
{
    if (bMatrixFormula)
    {
        SCCOL nCols = 0;
        SCROW nRows = 0;
        // In JumpMatrix context use its dimensions for the return matrix; the
        // formula cell range selected may differ, for example if the result is
        // to be transposed.
        if (GetStackType(1) == svJumpMatrix)
        {
            SCSIZE nC, nR;
            pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR);
            nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC));
            nRows = std::max<SCROW>(0, static_cast<SCROW>(nR));
        }
        else if (pMyFormulaCell)
            pMyFormulaCell->GetMatColsRows( nCols, nRows);

        if (nCols == 1 && nRows == 1)
        {
            // For compatibility with existing
            // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
            // default are executed in array context unless
            // FA.setPropertyValue("IsArrayFunction",False) was set, return a
            // scalar double instead of a 1x1 matrix object. tdf#128218
            PushDouble( RandomFunc( fFirst, fLast));
            return;
        }

        // ScViewFunc::EnterMatrix() might be asking for
        // ScFormulaCell::GetResultDimensions(), which here are none so create
        // a 1x1 matrix at least which exactly is the case when EnterMatrix()
        // asks for a not selected range.
        if (nCols == 0)
            nCols = 1;
        if (nRows == 0)
            nRows = 1;
        ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true );
        if (!pResMat)
            PushError( FormulaError::MatrixSize);
        else
        {
            for (SCCOL i=0; i < nCols; ++i)
            {
                for (SCROW j=0; j < nRows; ++j)
                {
                    pResMat->PutDouble( RandomFunc( fFirst, fLast),
                            static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
                }
            }
            PushMatrix( pResMat);
        }
    }
    else
    {
        PushDouble( RandomFunc( fFirst, fLast));
    }
}

void ScInterpreter::ScRandom()
{
    auto RandomFunc = [this]( doubledouble )
    {
        std::uniform_real_distribution<double> dist(0.0, 1.0);
        return dist(mrContext.aRNG);
    };
    ScRandomImpl( RandomFunc, 0.0, 0.0 );
}

void ScInterpreter::ScRandArray()
{
    sal_uInt8 nParamCount = GetByte();
    // optional 5th para:
    // TRUE for a whole number
    // FALSE for a decimal number - default.
    bool bWholeNumber = false;
    if (nParamCount == 5)
        bWholeNumber = GetBoolWithDefault(false);

    // optional 4th para: The maximum value of the random numbers
    double fMax = 1.0;
    if (nParamCount >= 4)
        fMax = GetDoubleWithDefault(1.0);

    // optional 3rd para: The minimum value of the random numbers
    double fMin = 0.0;
    if (nParamCount >= 3)
        fMin = GetDoubleWithDefault(0.0);

    // optional 2nd para: The number of columns of the return array
    SCCOL nCols = 1;
    if (nParamCount >= 2)
        nCols = static_cast<SCCOL>(GetInt32WithDefault(1));

    // optional 1st para: The number of rows of the return array
    SCROW nRows = 1;
    if (nParamCount >= 1)
        nRows = static_cast<SCROW>(GetInt32WithDefault(1));

    if (bWholeNumber)
    {
        fMax = rtl::math::round(fMax, 0, rtl_math_RoundingMode_Up);
        fMin = rtl::math::round(fMin, 0, rtl_math_RoundingMode_Up);
    }

    if (nGlobalError != FormulaError::NONE || fMin > fMax || nCols <= 0 || nRows <= 0)
    {
        PushIllegalArgument();
        return;
    }

    if (bWholeNumber)
        fMax = std::nextafter(fMax + 1, -DBL_MAX);
    else
        fMax = std::nextafter(fMax, DBL_MAX);

    auto RandomFunc = [this](double fFirst, double fLast, bool bWholeNum)
        {
            std::uniform_real_distribution<double> dist(fFirst, fLast);
            if (bWholeNum)
                return floor(dist(mrContext.aRNG));
            else
                return dist(mrContext.aRNG);
        };

    if (nCols == 1 && nRows == 1)
    {
        PushDouble(RandomFunc(fMin, fMax, bWholeNumber));
        return;
    }

    ScMatrixRef pResMat = GetNewMat(static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true);
    if (!pResMat)
        PushError(FormulaError::MatrixSize);
    else
    {
        for (SCCOL i = 0; i < nCols; ++i)
        {
            for (SCROW j = 0; j < nRows; ++j)
            {
                pResMat->PutDouble(RandomFunc(fMin, fMax, bWholeNumber),
                    static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
            }
        }
        PushMatrix(pResMat);
    }
}

void ScInterpreter::ScRandbetween()
{
    if (!MustHaveParamCount( GetByte(), 2))
        return;

    // Same like scaddins/source/analysis/analysis.cxx
    // AnalysisAddIn::getRandbetween()
    double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
    double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
    if (nGlobalError != FormulaError::NONE || fMin > fMax)
    {
        PushIllegalArgument();
        return;
    }
    fMax = std::nextafter( fMax+1, -DBL_MAX);
    auto RandomFunc = [this]( double fFirst, double fLast )
    {
        std::uniform_real_distribution<double> dist(fFirst, fLast);
        return floor(dist(mrContext.aRNG));
    };
    ScRandomImpl( RandomFunc, fMin, fMax);
}

void ScInterpreter::ScTrue()
{
    nFuncFmtType = SvNumFormatType::LOGICAL;
    PushInt(1);
}

void ScInterpreter::ScFalse()
{
    nFuncFmtType = SvNumFormatType::LOGICAL;
    PushInt(0);
}

void ScInterpreter::ScDeg()
{
    PushDouble(basegfx::rad2deg(GetDouble()));
}

void ScInterpreter::ScRad()
{
    PushDouble(basegfx::deg2rad(GetDouble()));
}

void ScInterpreter::ScSin()
{
    PushDouble(::rtl::math::sin(GetDouble()));
}

void ScInterpreter::ScCos()
{
    PushDouble(::rtl::math::cos(GetDouble()));
}

void ScInterpreter::ScTan()
{
    PushDouble(::rtl::math::tan(GetDouble()));
}

void ScInterpreter::ScCot()
{
--> --------------------

--> maximum size reached

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

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.32 Sekunden  (vorverarbeitet)  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge