/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

// INCLUDE ---------------------------------------------------------------

#include <sfx2/linkmgr.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/objsh.hxx>
#include <svl/stritem.hxx>
#include <svl/zforlist.hxx>
#include <rtl/logfile.hxx>
#include <sal/macros.h>

#include "interpre.hxx"
#include "attrib.hxx"
#include "sc.hrc"
#include "ddelink.hxx"
#include "scmatrix.hxx"
#include "compiler.hxx"
#include "cell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "docoptio.hxx"
#include "unitconv.hxx"
#include "globstr.hrc"
#include "hints.hxx"
#include "dpobject.hxx"
#include "postit.hxx"

#include <string.h>
#include <math.h>

using namespace formula;
// STATIC DATA -----------------------------------------------------------

#define D_TIMEFACTOR              86400.0
#define SCdEpsilon                1.0E-7

//-----------------------------------------------------------------------------
// Datum und Zeit
//-----------------------------------------------------------------------------

double ScInterpreter::GetDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bStrict )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDateSerial" );
    if ( nYear < 100 && !bStrict )
        nYear = pFormatter->ExpandTwoDigitYear( nYear );
    // Do not use a default Date ctor here because it asks system time with a
    // performance penalty.
    sal_Int16 nY, nM, nD;
    if (bStrict)
        nY = nYear, nM = nMonth, nD = nDay;
    else
    {
        if (nMonth > 0)
        {
            nY = nYear + (nMonth-1) / 12;
            nM = ((nMonth-1) % 12) + 1;
        }
        else
        {
            nY = nYear + (nMonth-12) / 12;
            nM = 12 - (-nMonth) % 12;
        }
        nD = 1;
    }
    Date aDate( nD, nM, nY);
    if (!bStrict)
        aDate += nDay - 1;
    if (aDate.IsValid())
        return (double) (aDate - *(pFormatter->GetNullDate()));
    else
    {
        SetError(errNoValue);
        return 0;
    }
}

//-----------------------------------------------------------------------------
// Funktionen
//-----------------------------------------------------------------------------

void ScInterpreter::ScGetActDate()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetActDate" );
    nFuncFmtType = NUMBERFORMAT_DATE;
    Date aActDate;
    long nDiff = aActDate - *(pFormatter->GetNullDate());
    PushDouble((double) nDiff);
}

void ScInterpreter::ScGetActTime()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetActTime" );
    nFuncFmtType = NUMBERFORMAT_DATETIME;
    Date aActDate;
    long nDiff = aActDate - *(pFormatter->GetNullDate());
    Time aActTime;
    double nTime = ((double)aActTime.Get100Sec() / 100 +
                    (double)(aActTime.GetSec()        +
                            (aActTime.GetMin()  * 60) +
                            (aActTime.GetHour() * 3600))) / D_TIMEFACTOR;
    PushDouble( (double) nDiff + nTime );
}

void ScInterpreter::ScGetYear()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetYear" );
    Date aDate = *(pFormatter->GetNullDate());
    aDate += (long) ::rtl::math::approxFloor(GetDouble());
    PushDouble( (double) aDate.GetYear() );
}

void ScInterpreter::ScGetMonth()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetMonth" );
    Date aDate = *(pFormatter->GetNullDate());
    aDate += (long) ::rtl::math::approxFloor(GetDouble());
    PushDouble( (double) aDate.GetMonth() );
}

void ScInterpreter::ScGetDay()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDay" );
    Date aDate = *(pFormatter->GetNullDate());
    aDate += (long)::rtl::math::approxFloor(GetDouble());
    PushDouble((double) aDate.GetDay());
}

void ScInterpreter::ScGetMin()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetMin" );
    double fTime = GetDouble();
    fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
    long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 3600;
    PushDouble( (double) (nVal/60) );
}

void ScInterpreter::ScGetSec()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetSec" );
    double fTime = GetDouble();
    fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
    long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 60;
    PushDouble( (double) nVal );
}

void ScInterpreter::ScGetHour()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetHour" );
    double fTime = GetDouble();
    fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
    long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) / 3600;
    PushDouble((double) nVal);
}

void ScInterpreter::ScGetDateValue()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDateValue" );
    String aInputString = GetString();
    sal_uInt32 nFIndex = 0;                 // damit default Land/Spr.
    double fVal;
    if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
    {
        short eType = pFormatter->GetType(nFIndex);
        if (eType == NUMBERFORMAT_DATE || eType == NUMBERFORMAT_DATETIME)
            PushDouble(::rtl::math::approxFloor(fVal));
        else
            PushIllegalArgument();
    }
    else
        PushIllegalArgument();
}

void ScInterpreter::ScGetDayOfWeek()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDayOfWeek" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    {
        short nFlag;
        if (nParamCount == 2)
            nFlag = (short) ::rtl::math::approxFloor(GetDouble());
        else
            nFlag = 1;

        Date aDate = *(pFormatter->GetNullDate());
        aDate += (long)::rtl::math::approxFloor(GetDouble());
        int nVal = (int) aDate.GetDayOfWeek();
        if (nFlag == 1)
        {
            if (nVal == 6)
                nVal = 1;
            else
                nVal += 2;
        }
        else if (nFlag == 2)
            nVal += 1;
        PushInt( nVal );
    }
}

void ScInterpreter::ScGetWeekOfYear()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetWeekOfYear" );
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        short nFlag = (short) ::rtl::math::approxFloor(GetDouble());

        Date aDate = *(pFormatter->GetNullDate());
        aDate += (long)::rtl::math::approxFloor(GetDouble());
        PushInt( (int) aDate.GetWeekOfYear( nFlag == 1 ? SUNDAY : MONDAY ));
    }
}

void ScInterpreter::ScEasterSunday()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEasterSunday" );
    nFuncFmtType = NUMBERFORMAT_DATE;
    if ( MustHaveParamCount( GetByte(), 1 ) )
    {
        sal_Int16 nDay, nMonth, nYear;
        nYear = (sal_Int16) ::rtl::math::approxFloor( GetDouble() );
        if ( nYear < 100 )
            nYear = pFormatter->ExpandTwoDigitYear( nYear );
        // don't worry, be happy :)
        int B,C,D,E,F,G,H,I,K,L,M,N,O;
        N = nYear % 19;
        B = int(nYear / 100);
        C = nYear % 100;
        D = int(B / 4);
        E = B % 4;
        F = int((B + 8) / 25);
        G = int((B - F + 1) / 3);
        H = (19 * N + B - D - G + 15) % 30;
        I = int(C / 4);
        K = C % 4;
        L = (32 + 2 * E + 2 * I - H - K) % 7;
        M = int((N + 11 * H + 22 * L) / 451);
        O = H + L - 7 * M + 114;
        nDay = sal::static_int_cast<sal_Int16>( O % 31 + 1 );
        nMonth = sal::static_int_cast<sal_Int16>( int(O / 31) );
        PushDouble( GetDateSerial( nYear, nMonth, nDay, true ) );
    }
}

void ScInterpreter::ScGetDate()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDate" );
    nFuncFmtType = NUMBERFORMAT_DATE;
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        sal_Int16 nDay   = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
        sal_Int16 nMonth = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
        sal_Int16 nYear  = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
        if (nYear < 0)
            PushIllegalArgument();
        else
        {
            PushDouble(GetDateSerial(nYear, nMonth, nDay, false));
        }
    }
}

void ScInterpreter::ScGetTime()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetTime" );
    nFuncFmtType = NUMBERFORMAT_TIME;
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        double nSec = GetDouble();
        double nMin = GetDouble();
        double nHour = GetDouble();
        PushDouble( ( (nHour * 3600) + (nMin * 60) + nSec ) / D_TIMEFACTOR );
    }
}

void ScInterpreter::ScGetDiffDate()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDiffDate" );
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double nDate2 = GetDouble();
        double nDate1 = GetDouble();
        PushDouble(nDate1 - nDate2);
    }
}

void ScInterpreter::ScGetDiffDate360()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDiffDate360" );
    /* Implementation follows
     * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf
     * Appendix B: Day-Count Bases, there are 7 different ways to calculate the
     * 30-days count. That document also claims that Excel implements the "PSA
     * 30" or "NASD 30" method (funny enough they also state that Excel is the
     * only tool that does so).
     *
     * Note that the definiton given in
     * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp
     * is _not_ the way how it is actually calculated by Excel (that would not
     * even match any of the 7 methods mentioned above) and would result in the
     * following test cases producing wrong results according to that appendix B:
     *
     * 28-Feb-95  31-Aug-95  181 instead of 180
     * 29-Feb-96  31-Aug-96  181 instead of 180
     * 30-Jan-96  31-Mar-96   61 instead of  60
     * 31-Jan-96  31-Mar-96   61 instead of  60
     *
     * Still, there is a difference between OOoCalc and Excel:
     * In Excel:
     * 02-Feb-99 31-Mar-00 results in  419
     * 31-Mar-00 02-Feb-99 results in -418
     * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
     */

    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 2, 3 ) )
    {
        sal_Bool bFlag;
        if (nParamCount == 3)
            bFlag = GetBool();
        else
            bFlag = false;
        double nDate2 = GetDouble();
        double nDate1 = GetDouble();
        if (nGlobalError)
            PushError( nGlobalError);
        else
        {
            double fSign;
            // #i84934# only for non-US European algorithm swap dates. Else
            // follow Excel's meaningless extrapolation for "interoperability".
            if (bFlag && (nDate2 < nDate1))
            {
                fSign = nDate1;
                nDate1 = nDate2;
                nDate2 = fSign;
                fSign = -1.0;
            }
            else
                fSign = 1.0;
            Date aDate1 = *(pFormatter->GetNullDate());
            aDate1 += (long) ::rtl::math::approxFloor(nDate1);
            Date aDate2 = *(pFormatter->GetNullDate());
            aDate2 += (long) ::rtl::math::approxFloor(nDate2);
            if (aDate1.GetDay() == 31)
                aDate1 -= (sal_uLong) 1;
            else if (!bFlag)
            {
                if (aDate1.GetMonth() == 2)
                {
                    switch ( aDate1.GetDay() )
                    {
                        case 28 :
                            if ( !aDate1.IsLeapYear() )
                                aDate1.SetDay(30);
                        break;
                        case 29 :
                            aDate1.SetDay(30);
                        break;
                    }
                }
            }
            if (aDate2.GetDay() == 31)
            {
                if (!bFlag )
                {
                    if (aDate1.GetDay() == 30)
                        aDate2 -= (sal_uLong) 1;
                }
                else
                    aDate2.SetDay(30);
            }
            PushDouble( fSign * (double)
                (  (double) aDate2.GetDay() + (double) aDate2.GetMonth() * 30.0 +
                   (double) aDate2.GetYear() * 360.0
                 - (double) aDate1.GetDay() - (double) aDate1.GetMonth() * 30.0
                 - (double)aDate1.GetYear() * 360.0) );
        }
    }
}

void ScInterpreter::ScGetTimeValue()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetTimeValue" );
    String aInputString = GetString();
    sal_uInt32 nFIndex = 0;                 // damit default Land/Spr.
    double fVal;
    if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
    {
        short eType = pFormatter->GetType(nFIndex);
        if (eType == NUMBERFORMAT_TIME || eType == NUMBERFORMAT_DATETIME)
        {
            double fDateVal = rtl::math::approxFloor(fVal);
            double fTimeVal = fVal - fDateVal;
            PushDouble(fTimeVal);
        }
        else
            PushIllegalArgument();
    }
    else
        PushIllegalArgument();
}

void ScInterpreter::ScPlusMinus()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPlusMinus" );
    double nVal = GetDouble();
    short n = 0;
    if (nVal < 0.0)
        n = -1;
    else if (nVal > 0.0)
        n = 1;
    PushInt( n );
}

void ScInterpreter::ScAbs()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAbs" );
    PushDouble(fabs(GetDouble()));
}

void ScInterpreter::ScInt()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScInt" );
    PushDouble(::rtl::math::approxFloor(GetDouble()));
}


void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::RoundNumber" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    {
        double fVal = 0.0;
        if (nParamCount == 1)
            fVal = ::rtl::math::round( GetDouble(), 0, eMode );
        else
        {
            sal_Int32 nDec = (sal_Int32) ::rtl::math::approxFloor(GetDouble());
            if( nDec < -20 || nDec > 20 )
                PushIllegalArgument();
            else
                fVal = ::rtl::math::round( GetDouble(), (short)nDec, eMode );
        }
        PushDouble(fVal);
    }
}

void ScInterpreter::ScRound()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRound" );
    RoundNumber( rtl_math_RoundingMode_Corrected );
}

void ScInterpreter::ScRoundDown()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRoundDown" );
    RoundNumber( rtl_math_RoundingMode_Down );
}

void ScInterpreter::ScRoundUp()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRoundUp" );
    RoundNumber( rtl_math_RoundingMode_Up );
}

void ScInterpreter::ScCeil()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCeil" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 2, 3 ) )
    {
        sal_Bool bAbs = ( nParamCount == 3 ? GetBool() : false );
        double fDec = GetDouble();
        double fVal = GetDouble();
        if ( fDec == 0.0 )
            PushInt(0);
        else if (fVal*fDec < 0.0)
            PushIllegalArgument();
        else
        {
            if ( !bAbs && fVal < 0.0 )
                PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec);
            else
                PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
        }
    }
}

void ScInterpreter::ScFloor()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFloor" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 2, 3 ) )
    {
        sal_Bool bAbs = ( nParamCount == 3 ? GetBool() : false );
        double fDec = GetDouble();
        double fVal = GetDouble();
        if ( fDec == 0.0 )
            PushInt(0);
        else if (fVal*fDec < 0.0)
            PushIllegalArgument();
        else
        {
            if ( !bAbs && fVal < 0.0 )
                PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
            else
                PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec);
        }
    }
}

void ScInterpreter::ScEven()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEven" );
    double fVal = GetDouble();
    if (fVal < 0.0)
        PushDouble(::rtl::math::approxFloor(fVal/2.0) * 2.0);
    else
        PushDouble(::rtl::math::approxCeil(fVal/2.0) * 2.0);
}

void ScInterpreter::ScOdd()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOdd" );
    double fVal = GetDouble();
    if (fVal >= 0.0)
    {
        fVal = ::rtl::math::approxCeil(fVal);
        if (fmod(fVal, 2.0) == 0.0)
            fVal += 1.0;
    }
    else
    {
        fVal = ::rtl::math::approxFloor(fVal);
        if (fmod(fVal, 2.0) == 0.0)
            fVal -= 1.0;
    }
    PushDouble(fVal);
}

void ScInterpreter::ScArcTan2()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTan2" );
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double nVal2 = GetDouble();
        double nVal1 = GetDouble();
        PushDouble(atan2(nVal2, nVal1));
    }
}

void ScInterpreter::ScLog()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLog" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    {
        double nBase;
        if (nParamCount == 2)
            nBase = GetDouble();
        else
            nBase = 10.0;
        double nVal = GetDouble();
        if (nVal > 0.0 && nBase > 0.0 && nBase != 1.0)
            PushDouble(log(nVal) / log(nBase));
        else
            PushIllegalArgument();
    }
}

void ScInterpreter::ScLn()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLn" );
    double fVal = GetDouble();
    if (fVal > 0.0)
        PushDouble(log(fVal));
    else
        PushIllegalArgument();
}

void ScInterpreter::ScLog10()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLog10" );
    double fVal = GetDouble();
    if (fVal > 0.0)
        PushDouble(log10(fVal));
    else
        PushIllegalArgument();
}

void ScInterpreter::ScNPV()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNPV" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    short nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 2, 31 ) )
    {
        double nVal = 0.0;
        // Wir drehen den Stack um!!
        FormulaToken* pTemp[ 31 ];
        for( short i = 0; i < nParamCount; i++ )
            pTemp[ i ] = pStack[ sp - i - 1 ];
        memcpy( &pStack[ sp - nParamCount ], pTemp, nParamCount * sizeof( FormulaToken* ) );
        if (nGlobalError == 0)
        {
            double  nCount = 1.0;
            double  nZins = GetDouble();
            --nParamCount;
            size_t nRefInList = 0;
            ScRange aRange;
            while (nParamCount-- > 0)
            {
                switch (GetStackType())
                {
                    case svDouble :
                    {
                        nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount));
                        nCount++;
                    }
                    break;
                    case svSingleRef :
                    {
                        nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount));
                        nCount++;
                    }
                    break;
                    case svDoubleRef :
                    case svRefList :
                    {
                        sal_uInt16 nErr = 0;
                        double nCellVal;
                        PopDoubleRef( aRange, nParamCount, nRefInList);
                        ScValueIterator aValIter(pDok, aRange, glSubTotal);
                        if (aValIter.GetFirst(nCellVal, nErr))
                        {
                            nVal += (nCellVal / pow(1.0 + nZins, (double)nCount));
                            nCount++;
                            while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr))
                            {
                                nVal += (nCellVal / pow(1.0 + nZins, (double)nCount));
                                nCount++;
                            }
                            SetError(nErr);
                        }
                    }
                    break;
                    default : SetError(errIllegalParameter); break;
                }
            }
        }
        PushDouble(nVal);
    }
}

void ScInterpreter::ScIRR()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIRR" );
    double fSchaetzwert;
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
        return;
    if (nParamCount == 2)
        fSchaetzwert = GetDouble();
    else
        fSchaetzwert = 0.1;
    sal_uInt16 sPos = sp;                       // Stack-Position merken
    double fEps = 1.0;
    double x, xNeu, fWert, fZaehler, fNenner, nCount;
    if (fSchaetzwert == -1.0)
        x = 0.1;                            // default gegen Nulldivisionen
    else
        x = fSchaetzwert;                   // Startwert
    switch (GetStackType())
    {
        case svDoubleRef :
        break;
        default:
        {
            PushIllegalParameter();
            return;
        }
    }
    const sal_uInt16 nIterationsMax = 20;
    sal_uInt16 nItCount = 0;
    ScRange aRange;
    while (fEps > SCdEpsilon && nItCount < nIterationsMax)
    {                                       // Newton-Verfahren:
        sp = sPos;                          // Stack zuruecksetzen
        nCount = 0.0;
        fZaehler = 0.0;
        fNenner = 0.0;
        sal_uInt16 nErr = 0;
        PopDoubleRef( aRange );
        ScValueIterator aValIter(pDok, aRange, glSubTotal);
        if (aValIter.GetFirst(fWert, nErr))
        {
            fZaehler +=           fWert / pow(1.0+x,(double)nCount);
            fNenner  += -nCount * fWert / pow(1.0+x,nCount+1.0);
            nCount++;
            while ((nErr == 0) && aValIter.GetNext(fWert, nErr))
            {
                fZaehler +=           fWert / pow(1.0+x,(double)nCount);
                fNenner  += -nCount * fWert / pow(1.0+x,nCount+1.0);
                nCount++;
            }
            SetError(nErr);
        }
        xNeu = x - fZaehler / fNenner;  // x(i+1) = x(i)-f(x(i))/f'(x(i))
        nItCount++;
        fEps = fabs(xNeu - x);
        x = xNeu;
    }
    if (fSchaetzwert == 0.0 && fabs(x) < SCdEpsilon)
        x = 0.0;                        // auf Null normieren
    if (fEps < SCdEpsilon)
        PushDouble(x);
    else
        PushError( errNoConvergence);
}

void ScInterpreter::ScMIRR()
{   // range_of_values ; rate_invest ; rate_reinvest
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    if( MustHaveParamCount( GetByte(), 3 ) )
    {
        double fRate1_reinvest = GetDouble() + 1;
        double fRate1_invest = GetDouble() + 1;

        ScRange aRange;
        PopDoubleRef( aRange );

        if( nGlobalError )
            PushError( nGlobalError);
        else
        {
            double fNPV_reinvest = 0.0;
            double fPow_reinvest = 1.0;
            double fNPV_invest = 0.0;
            double fPow_invest = 1.0;
            ScValueIterator aValIter( pDok, aRange, glSubTotal );
            double fCellValue;
            sal_uLong nCount = 0;
            sal_uInt16 nIterError = 0;

            sal_Bool bLoop = aValIter.GetFirst( fCellValue, nIterError );
            while( bLoop )
            {
                if( fCellValue > 0.0 )          // reinvestments
                    fNPV_reinvest += fCellValue * fPow_reinvest;
                else if( fCellValue < 0.0 )     // investments
                    fNPV_invest += fCellValue * fPow_invest;
                fPow_reinvest /= fRate1_reinvest;
                fPow_invest /= fRate1_invest;
                nCount++;

                bLoop = aValIter.GetNext( fCellValue, nIterError );
            }
            if( nIterError )
                PushError( nIterError );
            else
            {
                double fResult = -fNPV_reinvest / fNPV_invest;
                fResult *= pow( fRate1_reinvest, (double) nCount - 1 );
                fResult = pow( fResult, 1.0 / (nCount - 1) );
                PushDouble( fResult - 1.0 );
            }
        }
    }
}


void ScInterpreter::ScISPMT()
{   // rate ; period ; total_periods ; invest
    if( MustHaveParamCount( GetByte(), 4 ) )
    {
        double fInvest = GetDouble();
        double fTotal = GetDouble();
        double fPeriod = GetDouble();
        double fRate = GetDouble();

        if( nGlobalError )
            PushError( nGlobalError);
        else
            PushDouble( fInvest * fRate * (fPeriod / fTotal - 1.0) );
    }
}


//----------------------- Finanzfunktionen ------------------------------------

double ScInterpreter::ScGetBw(double fZins, double fZzr, double fRmz,
                              double fZw, double fF)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMIRR" );
    double fBw;
    if (fZins == 0.0)
        fBw = fZw + fRmz * fZzr;
    else if (fF > 0.0)
        fBw = (fZw * pow(1.0 + fZins, -fZzr))
                + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr + 1.0)) / fZins)
                + fRmz;
    else
        fBw = (fZw * pow(1.0 + fZins, -fZzr))
                + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr)) / fZins);
    return -fBw;
}

void ScInterpreter::ScBW()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBW" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    double nRmz, nZzr, nZins, nZw = 0, nFlag = 0;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
        return;
    if (nParamCount == 5)
        nFlag = GetDouble();
    if (nParamCount >= 4)
        nZw   = GetDouble();
    nRmz  = GetDouble();
    nZzr  = GetDouble();
    nZins = GetDouble();
    PushDouble(ScGetBw(nZins, nZzr, nRmz, nZw, nFlag));
}

void ScInterpreter::ScDIA()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDIA" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    if ( MustHaveParamCount( GetByte(), 4 ) )
    {
        double nZr = GetDouble();
        double nDauer = GetDouble();
        double nRest = GetDouble();
        double nWert = GetDouble();
        double nDia = ((nWert - nRest) * (nDauer - nZr + 1.0)) /
                      ((nDauer * (nDauer + 1.0)) / 2.0);
        PushDouble(nDia);
    }
}

double ScInterpreter::ScGetGDA(double fWert, double fRest, double fDauer,
                double fPeriode, double fFaktor)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetGDA" );
    double fGda, fZins, fAlterWert, fNeuerWert;
    fZins = fFaktor / fDauer;
    if (fZins >= 1.0)
    {
        fZins = 1.0;
        if (fPeriode == 1.0)
            fAlterWert = fWert;
        else
            fAlterWert = 0.0;
    }
    else
        fAlterWert = fWert * pow(1.0 - fZins, fPeriode - 1.0);
    fNeuerWert = fWert * pow(1.0 - fZins, fPeriode);

    if (fNeuerWert < fRest)
        fGda = fAlterWert - fRest;
    else
        fGda = fAlterWert - fNeuerWert;
    if (fGda < 0.0)
        fGda = 0.0;
    return fGda;
}

void ScInterpreter::ScGDA()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGDA" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 4, 5 ) )
    {
        double nFaktor;
        if (nParamCount == 5)
            nFaktor = GetDouble();
        else
            nFaktor = 2.0;
        double nPeriode = GetDouble();
        double nDauer   = GetDouble();
        double nRest    = GetDouble();
        double nWert    = GetDouble();
        if (nWert < 0.0 || nRest < 0.0 || nFaktor <= 0.0 || nRest > nWert
                        || nPeriode < 1.0 || nPeriode > nDauer)
            PushIllegalArgument();
        else
            PushDouble(ScGetGDA(nWert, nRest, nDauer, nPeriode, nFaktor));
    }
}

void ScInterpreter::ScGDA2()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGDA2" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 4, 5 ) )
        return ;
    double nMonate;
    if (nParamCount == 4)
        nMonate = 12.0;
    else
        nMonate = ::rtl::math::approxFloor(GetDouble());
    double nPeriode = GetDouble();
    double nDauer = GetDouble();
    double nRest = GetDouble();
    double nWert = GetDouble();
    if (nMonate < 1.0 || nMonate > 12.0 || nDauer > 1200.0 || nRest < 0.0 ||
        nPeriode > (nDauer + 1.0) || nRest > nWert || nWert < 0.0)
    {
        PushIllegalArgument();
        return;
    }
    double nAbRate = 1.0 - pow(nRest / nWert, 1.0 / nDauer);
    nAbRate = ::rtl::math::approxFloor((nAbRate * 1000.0) + 0.5) / 1000.0;
    double nErsteAbRate = nWert * nAbRate * nMonate / 12.0;
    double nGda2 = 0.0;
    if (::rtl::math::approxFloor(nPeriode) == 1)
        nGda2 = nErsteAbRate;
    else
    {
        double nSummAbRate = nErsteAbRate;
        double nMin = nDauer;
        if (nMin > nPeriode) nMin = nPeriode;
        sal_uInt16 iMax = (sal_uInt16)::rtl::math::approxFloor(nMin);
        for (sal_uInt16 i = 2; i <= iMax; i++)
        {
            nGda2 = (nWert - nSummAbRate) * nAbRate;
            nSummAbRate += nGda2;
        }
        if (nPeriode > nDauer)
            nGda2 = ((nWert - nSummAbRate) * nAbRate * (12.0 - nMonate)) / 12.0;
    }
    PushDouble(nGda2);
}


double ScInterpreter::ScInterVDB(double fWert,double fRest,double fDauer,
                             double fDauer1,double fPeriode,double fFaktor)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScInterVDB" );
    double fVdb=0;
    double fIntEnd   = ::rtl::math::approxCeil(fPeriode);
    sal_uLong nLoopEnd   = (sal_uLong) fIntEnd;

    double fTerm, fLia;
    double fRestwert = fWert - fRest;
    sal_Bool bNowLia = false;

    double fGda;
    sal_uLong i;
    fLia=0;
    for ( i = 1; i <= nLoopEnd; i++)
    {
        if(!bNowLia)
        {
            fGda = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor);
            fLia = fRestwert/ (fDauer1 - (double) (i-1));

            if (fLia > fGda)
            {
                fTerm = fLia;
                bNowLia = sal_True;
            }
            else
            {
                fTerm = fGda;
                fRestwert -= fGda;
            }
        }
        else
        {
            fTerm = fLia;
        }

        if ( i == nLoopEnd)
            fTerm *= ( fPeriode + 1.0 - fIntEnd );

        fVdb += fTerm;
    }
    return fVdb;
}


inline double DblMin( double a, double b )
{
    return (a < b) ? a : b;
}

void ScInterpreter::ScVDB()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVDB" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 5, 7 ) )
    {
        double fWert, fRest, fDauer, fAnfang, fEnde, fFaktor, fVdb = 0.0;
        sal_Bool bFlag;
        if (nParamCount == 7)
            bFlag = GetBool();
        else
            bFlag = false;
        if (nParamCount >= 6)
            fFaktor = GetDouble();
        else
            fFaktor = 2.0;
        fEnde   = GetDouble();
        fAnfang = GetDouble();
        fDauer  = GetDouble();
        fRest   = GetDouble();
        fWert   = GetDouble();
        if (fAnfang < 0.0 || fEnde < fAnfang || fEnde > fDauer || fWert < 0.0
                          || fRest > fWert || fFaktor <= 0.0)
            PushIllegalArgument();
        else
        {
            double fIntStart = ::rtl::math::approxFloor(fAnfang);
            double fIntEnd   = ::rtl::math::approxCeil(fEnde);
            sal_uLong nLoopStart = (sal_uLong) fIntStart;
            sal_uLong nLoopEnd   = (sal_uLong) fIntEnd;

            fVdb = 0.0;
            if (bFlag)
            {
                for (sal_uLong i = nLoopStart + 1; i <= nLoopEnd; i++)
                {
                    double fTerm = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor);

                    //  Teilperioden am Anfang / Ende beruecksichtigen:
                    if ( i == nLoopStart+1 )
                        fTerm *= ( DblMin( fEnde, fIntStart + 1.0 ) - fAnfang );
                    else if ( i == nLoopEnd )
                        fTerm *= ( fEnde + 1.0 - fIntEnd );

                    fVdb += fTerm;
                }
            }
            else
            {

                double fDauer1=fDauer;

                //@Die Frage aller Fragen: "Ist das hier richtig"
                if(!::rtl::math::approxEqual(fAnfang,::rtl::math::approxFloor(fAnfang)))
                {
                    if(fFaktor>1)
                    {
                        if(fAnfang>fDauer/2 || ::rtl::math::approxEqual(fAnfang,fDauer/2))
                        {
                            double fPart=fAnfang-fDauer/2;
                            fAnfang=fDauer/2;
                            fEnde-=fPart;
                            fDauer1+=1;
                        }
                    }
                }

                fWert-=ScInterVDB(fWert,fRest,fDauer,fDauer1,fAnfang,fFaktor);
                fVdb=ScInterVDB(fWert,fRest,fDauer,fDauer-fAnfang,fEnde-fAnfang,fFaktor);
            }
        }
        PushDouble(fVdb);
    }
}

void ScInterpreter::ScLaufz()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLaufz" );
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        double nZukunft = GetDouble();
        double nGegenwart = GetDouble();
        double nZins = GetDouble();
        PushDouble(log(nZukunft / nGegenwart) / log(1.0 + nZins));
    }
}

void ScInterpreter::ScLIA()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLIA" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        double nDauer = GetDouble();
        double nRest = GetDouble();
        double nWert = GetDouble();
        PushDouble((nWert - nRest) / nDauer);
    }
}

double ScInterpreter::ScGetRmz(double fZins, double fZzr, double fBw,
                       double fZw, double fF)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetRmz" );
    double fRmz;
    if (fZins == 0.0)
        fRmz = (fBw + fZw) / fZzr;
    else
    {
        double fTerm = pow(1.0 + fZins, fZzr);
        if (fF > 0.0)
            fRmz = (fZw * fZins / (fTerm - 1.0)
                        + fBw * fZins / (1.0 - 1.0 / fTerm)) / (1.0+fZins);
        else
            fRmz = fZw * fZins / (fTerm - 1.0)
                        + fBw * fZins / (1.0 - 1.0 / fTerm);
    }
    return -fRmz;
}

void ScInterpreter::ScRMZ()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRMZ" );
    double nZins, nZzr, nBw, nZw = 0, nFlag = 0;
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
        return;
    if (nParamCount == 5)
        nFlag = GetDouble();
    if (nParamCount >= 4)
        nZw   = GetDouble();
    nBw   = GetDouble();
    nZzr  = GetDouble();
    nZins = GetDouble();
    PushDouble(ScGetRmz(nZins, nZzr, nBw, nZw, nFlag));
}

void ScInterpreter::ScZGZ()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZGZ" );
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        double nZukunftswert = GetDouble();
        double nGegenwartswert = GetDouble();
        double nZeitraum = GetDouble();
        PushDouble(pow(nZukunftswert / nGegenwartswert, 1.0 / nZeitraum) - 1.0);
    }
}

double ScInterpreter::ScGetZw(double fZins, double fZzr, double fRmz,
                              double fBw, double fF)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetZw" );
    double fZw;
    if (fZins == 0.0)
        fZw = fBw + fRmz * fZzr;
    else
    {
        double fTerm = pow(1.0 + fZins, fZzr);
        if (fF > 0.0)
            fZw = fBw * fTerm + fRmz*(1.0 + fZins)*(fTerm - 1.0)/fZins;
        else
            fZw = fBw * fTerm + fRmz*(fTerm - 1.0)/fZins;
    }
    return -fZw;
}

void ScInterpreter::ScZW()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZW" );
    double nZins, nZzr, nRmz, nBw = 0, nFlag = 0;
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
        return;
    if (nParamCount == 5)
        nFlag = GetDouble();
    if (nParamCount >= 4)
        nBw   = GetDouble();
    nRmz  = GetDouble();
    nZzr  = GetDouble();
    nZins = GetDouble();
    PushDouble(ScGetZw(nZins, nZzr, nRmz, nBw, nFlag));
}

void ScInterpreter::ScZZR()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZZR" );
    double nZins, nRmz, nBw, nZw = 0, nFlag = 0;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
        return;
    if (nParamCount == 5)
        nFlag = GetDouble();
    if (nParamCount >= 4)
        nZw   = GetDouble();
    nBw   = GetDouble();
    nRmz  = GetDouble();
    nZins = GetDouble();
    if (nZins == 0.0)
        PushDouble(-(nBw + nZw)/nRmz);
    else if (nFlag > 0.0)
        PushDouble(log(-(nZins*nZw-nRmz*(1.0+nZins))/(nZins*nBw+nRmz*(1.0+nZins)))
                  /log(1.0+nZins));
    else
        PushDouble(log(-(nZins*nZw-nRmz)/(nZins*nBw+nRmz))/log(1.0+nZins));
}

bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
                                   double fFv, double fPayType, double & fGuess )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::RateIteration" );
    // See also #i15090#
    // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i))
    // This solution handles integer and non-integer values of Nper different.
    // If ODFF will constraint Nper to integer, the distinction of cases can be
    // removed; only the integer-part is needed then.
    bool bValid = true, bFound = false;
    double fX, fXnew, fTerm, fTermDerivation;
    double fGeoSeries, fGeoSeriesDerivation;
    const sal_uInt16 nIterationsMax = 150;
    sal_uInt16 nCount = 0;
    const double fEpsilonSmall = 1.0E-14;
    // convert any fPayType situation to fPayType == zero situation
    fFv = fFv - fPayment * fPayType;
    fPv = fPv + fPayment * fPayType;
    if (fNper == ::rtl::math::round( fNper, 0, rtl_math_RoundingMode_Corrected ))
    { // Nper is an integer value
        fX = fGuess;
        double fPowN, fPowNminus1;  // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
        while (!bFound && nCount < nIterationsMax)
        {
            fPowNminus1 = pow( 1.0+fX, fNper-1.0);
            fPowN = fPowNminus1 * (1.0+fX);
            if (rtl::math::approxEqual( fabs(fX), 0.0))
            {
                fGeoSeries = fNper;
                fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
            }
            else
            {
                fGeoSeries = (fPowN-1.0)/fX;
                fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;
            }
            fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;
            fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation;
            if (fabs(fTerm) < fEpsilonSmall)
                bFound = true;  // will catch root which is at an extreme
            else
            {
                if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
                    fXnew = fX + 1.1 * SCdEpsilon;  // move away from zero slope
                else
                    fXnew = fX - fTerm / fTermDerivation;
            nCount++;
            // more accuracy not possible in oscillating cases
            bFound = (fabs(fXnew - fX) < SCdEpsilon);
            fX = fXnew;
            }
        }
        // Gnumeric returns roots < -1, Excel gives an error in that cases,
        // ODFF says nothing about it. Enable the statement, if you want Excel's
        // behavior
        //bValid =(fX >=-1.0);
    }
    else
    { // Nper is not an integer value.
        fX = (fGuess < -1.0) ? -1.0 : fGuess;   // start with a valid fX
        while (bValid && !bFound && nCount < nIterationsMax)
        {
            if (rtl::math::approxEqual( fabs(fX), 0.0))
            {
                fGeoSeries = fNper;
                fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
            }
            else
            {
                fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;
                fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;
            }
            fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;
            fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation;
            if (fabs(fTerm) < fEpsilonSmall)
                bFound = true;  // will catch root which is at an extreme
            else
            {
                if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
                    fXnew = fX + 1.1 * SCdEpsilon;  // move away from zero slope
                else
                    fXnew = fX - fTerm / fTermDerivation;
            nCount++;
             // more accuracy not possible in oscillating cases
            bFound = (fabs(fXnew - fX) < SCdEpsilon);
            fX = fXnew;
            bValid = (fX >= -1.0);  // otherwise pow(1.0+fX,fNper) will fail
            }
        }
    }
    fGuess = fX;    // return approximate root
    return bValid && bFound;
}

// In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess)
void ScInterpreter::ScZins()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZins" );
    double fPv, fPayment, fNper;
    // defaults for missing arguments, see ODFF spec
    double fFv = 0, fPayType = 0, fGuess = 0.1;
    bool bValid = true;
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
        return;
    if (nParamCount == 6)
        fGuess = GetDouble();
    if (nParamCount >= 5)
        fPayType = GetDouble();
    if (nParamCount >= 4)
        fFv = GetDouble();
    fPv = GetDouble();
    fPayment = GetDouble();
    fNper = GetDouble();
    if (fNper <= 0.0) // constraint from ODFF spec
    {
        PushIllegalArgument();
        return;
    }
    // other values for fPayType might be meaningful,
    // ODFF spec is not clear yet, enable statement if you want only 0 and 1
    //if (fPayType != 0.0) fPayType = 1.0;
    bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
    if (!bValid)
        SetError(errNoConvergence);
    PushDouble(fGuess);
}

double ScInterpreter::ScGetZinsZ(double fZins, double fZr, double fZzr, double fBw,
                                 double fZw, double fF, double& fRmz)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetZinsZ" );
    fRmz = ScGetRmz(fZins, fZzr, fBw, fZw, fF);     // fuer kapz auch bei fZr == 1
    double fZinsZ;
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    if (fZr == 1.0)
    {
        if (fF > 0.0)
            fZinsZ = 0.0;
        else
            fZinsZ = -fBw;
    }
    else
    {
        if (fF > 0.0)
            fZinsZ = ScGetZw(fZins, fZr-2.0, fRmz, fBw, 1.0) - fRmz;
        else
            fZinsZ = ScGetZw(fZins, fZr-1.0, fRmz, fBw, 0.0);
    }
    return fZinsZ * fZins;
}

void ScInterpreter::ScZinsZ()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZinsZ" );
    double nZins, nZr, nZzr, nBw, nZw = 0, nFlag = 0;
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
        return;
    if (nParamCount == 6)
        nFlag = GetDouble();
    if (nParamCount >= 5)
        nZw   = GetDouble();
    nBw   = GetDouble();
    nZzr  = GetDouble();
    nZr   = GetDouble();
    nZins = GetDouble();
    if (nZr < 1.0 || nZr > nZzr)
        PushIllegalArgument();
    else
    {
        double nRmz;
        PushDouble(ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz));
    }
}

void ScInterpreter::ScKapz()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKapz" );
    double nZins, nZr, nZzr, nBw, nZw = 0, nFlag = 0;
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    sal_uInt8 nParamCount = GetByte();
    if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
        return;
    if (nParamCount == 6)
        nFlag = GetDouble();
    if (nParamCount >= 5)
        nZw   = GetDouble();
    nBw   = GetDouble();
    nZzr  = GetDouble();
    nZr   = GetDouble();
    nZins = GetDouble();
    if (nZr < 1.0 || nZr > nZzr)
        PushIllegalArgument();
    else
    {
        double nRmz;
        double nZinsz = ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz);
        PushDouble(nRmz - nZinsz);
    }
}

void ScInterpreter::ScKumZinsZ()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKumZinsZ" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    if ( MustHaveParamCount( GetByte(), 6 ) )
    {
        double fZins, fZzr, fBw, fAnfang, fEnde, fF;
        fF      = GetDouble();
        fEnde   = ::rtl::math::approxFloor(GetDouble());
        fAnfang = ::rtl::math::approxFloor(GetDouble());
        fBw     = GetDouble();
        fZzr    = GetDouble();
        fZins   = GetDouble();
        if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 ||
            fEnde > fZzr  || fZzr <= 0.0 || fBw <= 0.0)
            PushIllegalArgument();
        else
        {
            sal_uLong nAnfang = (sal_uLong) fAnfang;
            sal_uLong nEnde = (sal_uLong) fEnde ;
            double fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF);
            double fZinsZ = 0.0;
            if (nAnfang == 1)
            {
                if (fF <= 0.0)
                    fZinsZ = -fBw;
                nAnfang++;
            }
            for (sal_uLong i = nAnfang; i <= nEnde; i++)
            {
                if (fF > 0.0)
                    fZinsZ += ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz;
                else
                    fZinsZ += ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0);
            }
            fZinsZ *= fZins;
            PushDouble(fZinsZ);
        }
    }
}

void ScInterpreter::ScKumKapZ()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKumKapZ" );
    nFuncFmtType = NUMBERFORMAT_CURRENCY;
    if ( MustHaveParamCount( GetByte(), 6 ) )
    {
        double fZins, fZzr, fBw, fAnfang, fEnde, fF;
        fF      = GetDouble();
        fEnde   = ::rtl::math::approxFloor(GetDouble());
        fAnfang = ::rtl::math::approxFloor(GetDouble());
        fBw     = GetDouble();
        fZzr    = GetDouble();
        fZins   = GetDouble();
        if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 ||
            fEnde > fZzr  || fZzr <= 0.0 || fBw <= 0.0)
            PushIllegalArgument();
        else
        {
            double fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF);
            double fKapZ = 0.0;
            sal_uLong nAnfang = (sal_uLong) fAnfang;
            sal_uLong nEnde = (sal_uLong) fEnde;
            if (nAnfang == 1)
            {
                if (fF <= 0.0)
                    fKapZ = fRmz + fBw * fZins;
                else
                    fKapZ = fRmz;
                nAnfang++;
            }
            for (sal_uLong i = nAnfang; i <= nEnde; i++)
            {
                if (fF > 0.0)
                    fKapZ += fRmz - (ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz) * fZins;
                else
                    fKapZ += fRmz - ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0) * fZins;
            }
            PushDouble(fKapZ);
        }
    }
}

void ScInterpreter::ScEffektiv()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEffektiv" );
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double fPerioden = GetDouble();
        double fNominal = GetDouble();
        if (fPerioden < 1.0 || fNominal <= 0.0)
            PushIllegalArgument();
        else
        {
            fPerioden = ::rtl::math::approxFloor(fPerioden);
            PushDouble(pow(1.0 + fNominal/fPerioden, fPerioden) - 1.0);
        }
    }
}

void ScInterpreter::ScNominal()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNominal" );
    nFuncFmtType = NUMBERFORMAT_PERCENT;
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double fPerioden = GetDouble();
        double fEffektiv = GetDouble();
        if (fPerioden < 1.0 || fEffektiv <= 0.0)
            PushIllegalArgument();
        else
        {
            fPerioden = ::rtl::math::approxFloor(fPerioden);
            PushDouble( (pow(fEffektiv + 1.0, 1.0 / fPerioden) - 1.0) * fPerioden );
        }
    }
}

void ScInterpreter::ScMod()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMod" );
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double fVal2 = GetDouble(); // Denominator
        double fVal1 = GetDouble(); // Numerator
        if (fVal2 == floor(fVal2))  // a pure integral number stored in double
        {
            double fResult = fmod(fVal1,fVal2);
            if ( (fResult != 0.0) &&
                ((fVal1 > 0.0 && fVal2 < 0.0) || (fVal1 < 0.0 && fVal2 > 0.0)))
                fResult += fVal2 ;
            PushDouble( fResult );
        }
        else
        {
            PushDouble( ::rtl::math::approxSub( fVal1,
                    ::rtl::math::approxFloor(fVal1 / fVal2) * fVal2));
        }
    }
}

/** (Goal Seek) Find a value of x that is a root of f(x)

    This function is used internally for the goal seek operation.  It uses the
    Regula Falsi (aka false position) algorithm to find a root of f(x).  The
    start value and the target value are to be given by the user in the
    goal seek dialog.  The f(x) in this case is defined as the formula in the
    formula cell minus target value.  This function may also perform additional
    search in the horizontal directions when the f(x) is discrete in order to
    ensure a non-zero slope necessary for deriving a subsequent x that is
    reasonably close to the root of interest.

    @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org)

    @see #i28955#
*/
void ScInterpreter::ScBackSolver()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBackSolver" );
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        sal_Bool bDoneIteration = false;
        ScAddress aValueAdr, aFormulaAdr;
        double fTargetVal = GetDouble();
        PopSingleRef( aFormulaAdr );
        PopSingleRef( aValueAdr );

        if (nGlobalError == 0)
        {
            ScBaseCell* pVCell = GetCell( aValueAdr );
            // CELLTYPE_NOTE: kein Value aber von Formel referiert
            sal_Bool bTempCell = (!pVCell || pVCell->GetCellType() == CELLTYPE_NOTE);
            ScBaseCell* pFCell = GetCell( aFormulaAdr );

            if ( ((pVCell && pVCell->GetCellType() == CELLTYPE_VALUE) || bTempCell)
                && pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA )
            {
                ScRange aVRange( aValueAdr, aValueAdr );    // fuer SetDirty
                double fSaveVal; // Original value to be restored later if necessary
                ScPostIt* pNote = 0;

                if ( bTempCell )
                {
                    pNote = pVCell ? pVCell->ReleaseNote() : 0;
                    fSaveVal = 0.0;
                    pVCell = new ScValueCell( fSaveVal );
                    pDok->PutCell( aValueAdr, pVCell );
                }
                else
                    fSaveVal = GetCellValue( aValueAdr, pVCell );

                const sal_uInt16 nMaxIter = 100;
                const double fEps = 1E-10;
                const double fDelta = 1E-6;

                double fBestX, fXPrev;
                double fBestF, fFPrev;
                fBestX = fXPrev = fSaveVal;

                ScFormulaCell* pFormula = (ScFormulaCell*) pFCell;
                ScValueCell* pValue = (ScValueCell*) pVCell;

                pFormula->Interpret();
                sal_Bool bError = ( pFormula->GetErrCode() != 0 );
                // bError always corresponds with fF

                fFPrev = pFormula->GetValue() - fTargetVal;

                fBestF = fabs( fFPrev );
                if ( fBestF < fDelta )
                    bDoneIteration = sal_True;

                double fX = fXPrev + fEps;
                double fF = fFPrev;
                double fSlope;

                sal_uInt16 nIter = 0;

                sal_Bool bHorMoveError = false;
                                                // Nach der Regula Falsi Methode
                while ( !bDoneIteration && ( nIter++ < nMaxIter ) )
                {
                    pValue->SetValue( fX );
                    pDok->SetDirty( aVRange );
                    pFormula->Interpret();
                    bError = ( pFormula->GetErrCode() != 0 );
                    fF = pFormula->GetValue() - fTargetVal;

                    if ( fF == fFPrev && !bError )
                    {
                        // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x)
                        // becomes different from the previous f(x).  This routine is needed
                        // when a given function is discrete, in which case the resulting slope
                        // may become zero which ultimately causes the goal seek operation
                        // to fail. #i28955#

                        sal_uInt16 nHorIter = 0;
                        const double fHorStepAngle = 5.0;
                        const double fHorMaxAngle = 80.0;
                        int nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle );
                        sal_Bool bDoneHorMove = false;

                        while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter )
                        {
                            double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter );
                            double fHorTangent = ::rtl::math::tan( fHorAngle * F_PI / 180 );

                            sal_uInt16 nIdx = 0;
                            while( nIdx++ < 2 && !bDoneHorMove )
                            {
                                double fHorX;
                                if ( nIdx == 1 )
                                    fHorX = fX + fabs(fF)*fHorTangent;
                                else
                                    fHorX = fX - fabs(fF)*fHorTangent;

                                pValue->SetValue( fHorX );
                                pDok->SetDirty( aVRange );
                                pFormula->Interpret();
                                bHorMoveError = ( pFormula->GetErrCode() != 0 );
                                if ( bHorMoveError )
                                    break;

                                fF = pFormula->GetValue() - fTargetVal;
                                if ( fF != fFPrev )
                                {
                                    fX = fHorX;
                                    bDoneHorMove = sal_True;
                                }
                            }
                        }
                        if ( !bDoneHorMove )
                            bHorMoveError = sal_True;
                    }

                    if ( bError )
                    {
                        // move closer to last valid value (fXPrev), keep fXPrev & fFPrev
                        double fDiff = ( fXPrev - fX ) / 2;
                        if (fabs(fDiff) < fEps)
                            fDiff = (fDiff < 0.0) ? - fEps : fEps;
                        fX += fDiff;
                    }
                    else if ( bHorMoveError )
                        break;
                    else if ( fabs(fF) < fDelta )
                    {
                        // converged to root
                        fBestX = fX;
                        bDoneIteration = sal_True;
                    }
                    else
                    {
                        if ( fabs(fF) + fDelta < fBestF )
                        {
                            fBestX = fX;
                            fBestF = fabs(fF);
                        }

                        if ( ( fXPrev - fX ) != 0 )
                        {
                            fSlope = ( fFPrev - fF ) / ( fXPrev - fX );
                            if ( fabs( fSlope ) < fEps )
                                fSlope = fSlope < 0.0 ? -fEps : fEps;
                        }
                        else
                            fSlope = fEps;

                        fXPrev = fX;
                        fFPrev = fF;
                        fX = fX - ( fF / fSlope );
                    }
                }

                // Try a nice rounded input value if possible.
                const double fNiceDelta = (bDoneIteration && fabs(fBestX) >= 1e-3 ? 1e-3 : fDelta);
                double nX = ::rtl::math::approxFloor((fBestX / fNiceDelta) + 0.5) * fNiceDelta;

                if ( bDoneIteration )
                {
                    pValue->SetValue( nX );
                    pDok->SetDirty( aVRange );
                    pFormula->Interpret();
                    if ( fabs( pFormula->GetValue() - fTargetVal ) > fabs( fF ) )
                        nX = fBestX;
                }
                else if ( bError || bHorMoveError )
                {
                    nX = fBestX;
                }
                if ( bTempCell )
                {
                    pVCell = pNote ? new ScNoteCell( pNote ) : 0;
                    pDok->PutCell( aValueAdr, pVCell );
                }
                else
                    pValue->SetValue( fSaveVal );
                pDok->SetDirty( aVRange );
                pFormula->Interpret();
                if ( !bDoneIteration )
                    SetError(NOTAVAILABLE);
                PushDouble(nX);
            }
            else
            {
                if ( !bDoneIteration )
                    SetError(NOTAVAILABLE);
                PushInt(0);         // falsche Zelltypen
            }
        }
        else
        {
            if ( !bDoneIteration )
                SetError(NOTAVAILABLE);
            PushInt(0);             // nGlobalError
        }
    }
}

void ScInterpreter::ScIntersect()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIntersect" );
    formula::FormulaTokenRef p2nd = PopToken();
    formula::FormulaTokenRef p1st = PopToken();

    if (nGlobalError || !p2nd || !p1st)
    {
        PushIllegalArgument();
        return;
    } // if (nGlobalError || !xT2 || !xT1)

    StackVar sv1 = p1st->GetType();
    StackVar sv2 = p2nd->GetType();
    if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
        (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
    {
        PushIllegalArgument();
        return;
    }

    ScToken* x1 = static_cast<ScToken*>(p1st.get());
    ScToken* x2 = static_cast<ScToken*>(p2nd.get());
    if (sv1 == svRefList || sv2 == svRefList)
    {
        // Now this is a bit nasty but it simplifies things, and having
        // intersections with lists isn't too common, if at all..
        // Convert a reference to list.
        ScToken* xt[2] = { x1, x2 };
        StackVar sv[2] = { sv1, sv2 };
        for (size_t i=0; i<2; ++i)
        {
            if (sv[i] == svSingleRef)
            {
                ScComplexRefData aRef;
                aRef.Ref1 = aRef.Ref2 = xt[i]->GetSingleRef();
                xt[i] = new ScRefListToken;
                xt[i]->GetRefList()->push_back( aRef);
            }
            else if (sv[i] == svDoubleRef)
            {
                ScComplexRefData aRef = xt[i]->GetDoubleRef();
                xt[i] = new ScRefListToken;
                xt[i]->GetRefList()->push_back( aRef);
            }
        }
        x1 = xt[0], x2 = xt[1];

        x1->CalcAbsIfRel( aPos);
        x2->CalcAbsIfRel( aPos);
        ScTokenRef xRes = new ScRefListToken;
        ScRefList* pRefList = xRes->GetRefList();
        ScRefList::const_iterator end1( x1->GetRefList()->end());
        ScRefList::const_iterator end2( x2->GetRefList()->end());
        for (ScRefList::const_iterator it1( x1->GetRefList()->begin());
                it1 != end1; ++it1)
        {
            const ScSingleRefData& r11 = (*it1).Ref1;
            const ScSingleRefData& r12 = (*it1).Ref2;
            for (ScRefList::const_iterator it2( x2->GetRefList()->begin());
                    it2 != end2; ++it2)
            {
                const ScSingleRefData& r21 = (*it2).Ref1;
                const ScSingleRefData& r22 = (*it2).Ref2;
                SCCOL nCol1 = ::std::max( r11.nCol, r21.nCol);
                SCROW nRow1 = ::std::max( r11.nRow, r21.nRow);
                SCTAB nTab1 = ::std::max( r11.nTab, r21.nTab);
                SCCOL nCol2 = ::std::min( r12.nCol, r22.nCol);
                SCROW nRow2 = ::std::min( r12.nRow, r22.nRow);
                SCTAB nTab2 = ::std::min( r12.nTab, r22.nTab);
                if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
                    ;   // nothing
                else
                {
                    ScComplexRefData aRef;
                    aRef.InitRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
                    pRefList->push_back( aRef);
                }
            }
        }
        size_t n = pRefList->size();
        if (!n)
            PushError( errNoRef);
        else if (n == 1)
        {
            const ScComplexRefData& rRef = (*pRefList)[0];
            if (rRef.Ref1 == rRef.Ref2)
                PushTempToken( new ScSingleRefToken( rRef.Ref1));
            else
                PushTempToken( new ScDoubleRefToken( rRef));
        }
        else
            PushTempToken( xRes.get());
    }
    else
    {
        ScToken* pt[2] = { x1, x2 };
        StackVar sv[2] = { sv1, sv2 };
        SCCOL nC1[2], nC2[2];
        SCROW nR1[2], nR2[2];
        SCTAB nT1[2], nT2[2];
        for (size_t i=0; i<2; ++i)
        {
            switch (sv[i])
            {
                case svSingleRef:
                case svDoubleRef:
                    pt[i]->CalcAbsIfRel( aPos);
                    {
                        const ScSingleRefData& r = pt[i]->GetSingleRef();
                        nC1[i] = r.nCol;
                        nR1[i] = r.nRow;
                        nT1[i] = r.nTab;
                    }
                    if (sv[i] == svDoubleRef)
                    {
                        const ScSingleRefData& r = pt[i]->GetSingleRef2();
                        nC2[i] = r.nCol;
                        nR2[i] = r.nRow;
                        nT2[i] = r.nTab;
                    }
                    else
                    {
                        nC2[i] = nC1[i];
                        nR2[i] = nR1[i];
                        nT2[i] = nT1[i];
                    }
                    break;
                default:
                    ;   // nothing, prevent compiler warning
            }
        }
        SCCOL nCol1 = ::std::max( nC1[0], nC1[1]);
        SCROW nRow1 = ::std::max( nR1[0], nR1[1]);
        SCTAB nTab1 = ::std::max( nT1[0], nT1[1]);
        SCCOL nCol2 = ::std::min( nC2[0], nC2[1]);
        SCROW nRow2 = ::std::min( nR2[0], nR2[1]);
        SCTAB nTab2 = ::std::min( nT2[0], nT2[1]);
        if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
            PushError( errNoRef);
        else if (nCol2 == nCol1 && nRow2 == nRow1 && nTab2 == nTab1)
            PushSingleRef( nCol1, nRow1, nTab1);
        else
            PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
    }
}


void ScInterpreter::ScRangeFunc()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRangeFunc" );
    formula::FormulaTokenRef x2 = PopToken();
    formula::FormulaTokenRef x1 = PopToken();

    if (nGlobalError || !x2 || !x1)
    {
        PushIllegalArgument();
        return;
    } // if (nGlobalError || !xT2 || !xT1)
    FormulaTokenRef xRes = ScToken::ExtendRangeReference( *x1, *x2, aPos, false);
    if (!xRes)
        PushIllegalArgument();
    else
        PushTempToken( xRes.get());
}


void ScInterpreter::ScUnionFunc()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnionFunc" );
    formula::FormulaTokenRef p2nd = PopToken();
    formula::FormulaTokenRef p1st = PopToken();

    if (nGlobalError || !p2nd || !p1st)
    {
        PushIllegalArgument();
        return;
    } // if (nGlobalError || !xT2 || !xT1)

    StackVar sv1 = p1st->GetType();
    StackVar sv2 = p2nd->GetType();
    if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
        (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
    {
        PushIllegalArgument();
        return;
    }

    ScToken* x1 = static_cast<ScToken*>(p1st.get());
    ScToken* x2 = static_cast<ScToken*>(p2nd.get());


    ScTokenRef xRes;
    // Append to an existing RefList if there is one.
    if (sv1 == svRefList)
    {
        xRes = x1;
        sv1 = svUnknown;    // mark as handled
    }
    else if (sv2 == svRefList)
    {
        xRes = x2;
        sv2 = svUnknown;    // mark as handled
    }
    else
        xRes = new ScRefListToken;
    ScRefList* pRes = xRes->GetRefList();
    ScToken* pt[2] = { x1, x2 };
    StackVar sv[2] = { sv1, sv2 };
    for (size_t i=0; i<2; ++i)
    {
        if (pt[i] == xRes)
            continue;
        switch (sv[i])
        {
            case svSingleRef:
                {
                    ScComplexRefData aRef;
                    aRef.Ref1 = aRef.Ref2 = pt[i]->GetSingleRef();
                    pRes->push_back( aRef);
                }
                break;
            case svDoubleRef:
                pRes->push_back( pt[i]->GetDoubleRef());
                break;
            case svRefList:
                {
                    const ScRefList* p = pt[i]->GetRefList();
                    ScRefList::const_iterator it( p->begin());
                    ScRefList::const_iterator end( p->end());
                    for ( ; it != end; ++it)
                    {
                        pRes->push_back( *it);
                    }
                }
                break;
            default:
                ;   // nothing, prevent compiler warning
        }
    }
    ValidateRef( *pRes);    // set #REF! if needed
    PushTempToken( xRes.get());
}


void ScInterpreter::ScCurrent()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCurrent" );
    FormulaTokenRef xTok( PopToken());
    if (xTok)
    {
        PushTempToken( xTok.get());
        PushTempToken( xTok.get());
    }
    else
        PushError( errUnknownStackVariable);
}

void ScInterpreter::ScStyle()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStyle" );
    sal_uInt8 nParamCount = GetByte();
    if (nParamCount >= 1 && nParamCount <= 3)
    {
        String aStyle2;                             // Vorlage nach Timer
        if (nParamCount >= 3)
            aStyle2 = GetString();
        long nTimeOut = 0;                          // Timeout
        if (nParamCount >= 2)
            nTimeOut = (long)(GetDouble()*1000.0);
        String aStyle1 = GetString();               // Vorlage fuer sofort

        if (nTimeOut < 0)
            nTimeOut = 0;

        //
        //  Request ausfuehren, um Vorlage anzuwenden
        //

        if ( !pDok->IsClipOrUndo() )
        {
            SfxObjectShell* pShell = pDok->GetDocumentShell();
            if (pShell)
            {
                //! notify object shell directly

                ScRange aRange(aPos);
                ScAutoStyleHint aHint( aRange, aStyle1, nTimeOut, aStyle2 );
                pShell->Broadcast( aHint );
            }
        }

        PushDouble(0.0);
    }
    else
        PushIllegalParameter();
}

ScDdeLink* lcl_GetDdeLink( sfx2::LinkManager* pLinkMgr,
                                const String& rA, const String& rT, const String& rI, sal_uInt8 nM )
{
    sal_uInt16 nCount = pLinkMgr->GetLinks().Count();
    for (sal_uInt16 i=0; i<nCount; i++ )
    {
        ::sfx2::SvBaseLink* pBase = *pLinkMgr->GetLinks()[i];
        if (pBase->ISA(ScDdeLink))
        {
            ScDdeLink* pLink = (ScDdeLink*)pBase;
            if ( pLink->GetAppl() == rA &&
                 pLink->GetTopic() == rT &&
                 pLink->GetItem() == rI &&
                 pLink->GetMode() == nM )
                return pLink;
        }
    }

    return NULL;
}

void ScInterpreter::ScDde()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDde" );
    //  Applikation, Datei, Bereich
    //  Application, Topic, Item

    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 3, 4 ) )
    {
        sal_uInt8 nMode = SC_DDE_DEFAULT;
        if (nParamCount == 4)
            nMode = (sal_uInt8) ::rtl::math::approxFloor(GetDouble());
        String aItem  = GetString();
        String aTopic = GetString();
        String aAppl  = GetString();

        if (nMode > SC_DDE_TEXT)
            nMode = SC_DDE_DEFAULT;

        //  temporary documents (ScFunctionAccess) have no DocShell
        //  and no LinkManager -> abort

        sfx2::LinkManager* pLinkMgr = pDok->GetLinkManager();
        if (!pLinkMgr)
        {
            PushNoValue();
            return;
        }

            //  Nach dem Laden muss neu interpretiert werden (Verknuepfungen aufbauen)

        if ( pMyFormulaCell->GetCode()->IsRecalcModeNormal() )
            pMyFormulaCell->GetCode()->SetRecalcModeOnLoad();

            //  solange der Link nicht ausgewertet ist, Idle abklemmen
            //  (um zirkulaere Referenzen zu vermeiden)

        sal_Bool bOldDis = pDok->IsIdleDisabled();
        pDok->DisableIdle( sal_True );

            //  Link-Objekt holen / anlegen

        ScDdeLink* pLink = lcl_GetDdeLink( pLinkMgr, aAppl, aTopic, aItem, nMode );

        //! Dde-Links (zusaetzlich) effizienter am Dokument speichern !!!!!
        //      ScDdeLink* pLink = pDok->GetDdeLink( aAppl, aTopic, aItem );

        sal_Bool bWasError = ( pMyFormulaCell->GetRawError() != 0 );

        if (!pLink)
        {
            pLink = new ScDdeLink( pDok, aAppl, aTopic, aItem, nMode );
            pLinkMgr->InsertDDELink( pLink, aAppl, aTopic, aItem );
            if ( pLinkMgr->GetLinks().Count() == 1 )                    // erster ?
            {
                SfxBindings* pBindings = pDok->GetViewBindings();
                if (pBindings)
                    pBindings->Invalidate( SID_LINKS );             // Link-Manager enablen
            }

                                    //! asynchron auswerten ???
            pLink->TryUpdate();     //  TryUpdate ruft Update nicht mehrfach auf

            // StartListening erst nach dem Update, sonst circular reference
            pMyFormulaCell->StartListening( *pLink );
        }
        else
        {
            pMyFormulaCell->StartListening( *pLink );
        }

        //  Wenn aus dem Reschedule beim Ausfuehren des Links ein Fehler
        //  (z.B. zirkulaere Referenz) entstanden ist, der vorher nicht da war,
        //  das Fehler-Flag zuruecksetzen:

        if ( pMyFormulaCell->GetRawError() && !bWasError )
            pMyFormulaCell->SetErrCode(0);

            //  Wert abfragen

        const ScMatrix* pLinkMat = pLink->GetResult();
        if (pLinkMat)
        {
            SCSIZE nC, nR;
            pLinkMat->GetDimensions(nC, nR);
            ScMatrixRef pNewMat = GetNewMat( nC, nR);
            if (pNewMat)
            {
                pLinkMat->MatCopy(*pNewMat);        // kopieren
                PushMatrix( pNewMat );
            }
            else
                PushIllegalArgument();
        }
        else
            PushNA();

        pDok->DisableIdle( bOldDis );
        pLinkMgr->CloseCachedComps();
    }
}

void ScInterpreter::ScBase()
{   // Value, Base [, MinLen]
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 2, 3 ) )
    {
        static const sal_Unicode pDigits[] = {
            '0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','I','J','K','L','M',
            'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            0
        };
        static const int nDigits = SAL_N_ELEMENTS(pDigits)-1;
        xub_StrLen nMinLen;
        if ( nParamCount == 3 )
        {
            double fLen = ::rtl::math::approxFloor( GetDouble() );
            if ( 1.0 <= fLen && fLen < STRING_MAXLEN )
                nMinLen = (xub_StrLen) fLen;
            else if ( fLen == 0.0 )
                nMinLen = 1;
            else
                nMinLen = 0;    // Error
        }
        else
            nMinLen = 1;
        double fBase = ::rtl::math::approxFloor( GetDouble() );
        double fVal = ::rtl::math::approxFloor( GetDouble() );
        double fChars = ((fVal > 0.0 && fBase > 0.0) ?
            (ceil( log( fVal ) / log( fBase ) ) + 2.0) :
            2.0);
        if ( fChars >= STRING_MAXLEN )
            nMinLen = 0;    // Error

        if ( !nGlobalError && nMinLen && 2 <= fBase && fBase <= nDigits && 0 <= fVal )
        {
            const xub_StrLen nConstBuf = 128;
            sal_Unicode aBuf[nConstBuf];
            xub_StrLen nBuf = Max( (xub_StrLen) fChars, (xub_StrLen) (nMinLen+1) );
            sal_Unicode* pBuf = (nBuf <= nConstBuf ? aBuf : new sal_Unicode[nBuf]);
            for ( xub_StrLen j = 0; j < nBuf; ++j )
            {
                pBuf[j] = '0';
            }
            sal_Unicode* p = pBuf + nBuf - 1;
            *p = 0;
            if ( fVal <= (sal_uLong)(~0) )
            {
                sal_uLong nVal = (sal_uLong) fVal;
                sal_uLong nBase = (sal_uLong) fBase;
                while ( nVal && p > pBuf )
                {
                    *--p = pDigits[ nVal % nBase ];
                    nVal /= nBase;
                }
                fVal = (double) nVal;
            }
            else
            {
                sal_Bool bDirt = false;
                while ( fVal && p > pBuf )
                {
//! mit fmod Rundungsfehler ab 2**48
//                  double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) );
// so ist es etwas besser
                    double fInt = ::rtl::math::approxFloor( fVal / fBase );
                    double fMult = fInt * fBase;
#if OSL_DEBUG_LEVEL > 1
                    // =BASIS(1e308;36) => GPF mit
                    // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult );
                    // trotz vorheriger Pruefung ob fVal >= fMult
                    double fDebug1 = fVal - fMult;
                    // fVal    := 7,5975311883090e+290
                    // fMult   := 7,5975311883090e+290
                    // fDebug1 := 1,3848924157003e+275  <- RoundOff-Error
                    // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE
                    double fDebug2 = ::rtl::math::approxSub( fVal, fMult );
                    // und ::rtl::math::approxSub( fVal, fMult ) == 0
                    double fDebug3 = ( fInt ? fVal / fInt : 0.0 );
                    // Nach dem strange fDebug1 und fVal < fMult  ist eigentlich
                    // fDebug2 == fBase, trotzdem wird das mit einem Vergleich
                    // nicht erkannt, dann schlaegt bDirt zu und alles wird wieder gut..

                    // prevent compiler warnings
                    (void)fDebug1; (void)fDebug2; (void)fDebug3;
#endif
                    size_t nDig;
                    if ( fVal < fMult )
                    {   // da ist was gekippt
                        bDirt = sal_True;
                        nDig = 0;
                    }
                    else
                    {
                        double fDig = ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal, fMult ) );
                        if ( bDirt )
                        {
                            bDirt = false;
                            --fDig;
                        }
                        if ( fDig <= 0.0 )
                            nDig = 0;
                        else if ( fDig >= fBase )
                            nDig = ((size_t) fBase) - 1;
                        else
                            nDig = (size_t) fDig;
                    }
                    *--p = pDigits[ nDig ];
                    fVal = fInt;
                }
            }
            if ( fVal )
                PushError( errStringOverflow );
            else
            {
                if ( nBuf - (p - pBuf) <= nMinLen )
                    p = pBuf + nBuf - 1 - nMinLen;
                PushStringBuffer( p );
            }
            if ( pBuf != aBuf )
                delete [] pBuf;
        }
        else
            PushIllegalArgument();
    }
}


void ScInterpreter::ScDecimal()
{   // Text, Base
    if ( MustHaveParamCount( GetByte(), 2 ) )
    {
        double fBase = ::rtl::math::approxFloor( GetDouble() );
        String aStr( GetString() );
        if ( !nGlobalError && 2 <= fBase && fBase <= 36 )
        {
            double fVal = 0.0;
            int nBase = (int) fBase;
            register const sal_Unicode* p = aStr.GetBuffer();
            while ( *p == ' ' || *p == '\t' )
                p++;        // strip leading white space
            if ( nBase == 16 )
            {   // evtl. hex-prefix strippen
                if ( *p == 'x' || *p == 'X' )
                    p++;
                else if ( *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X') )
                    p += 2;
            }
            while ( *p )
            {
                int n;
                if ( '0' <= *p && *p <= '9' )
                    n = *p - '0';
                else if ( 'A' <= *p && *p <= 'Z' )
                    n = 10 + (*p - 'A');
                else if ( 'a' <= *p && *p <= 'z' )
                    n = 10 + (*p - 'a');
                else
                    n = nBase;
                if ( nBase <= n )
                {
                    if ( *(p+1) == 0 &&
                            ( (nBase ==  2 && (*p == 'b' || *p == 'B'))
                            ||(nBase == 16 && (*p == 'h' || *p == 'H')) )
                        )
                        ;       // 101b und F00Dh sind ok
                    else
                    {
                        PushIllegalArgument();
                        return ;
                    }
                }
                else
                    fVal = fVal * fBase + n;
                p++;

            }
            PushDouble( fVal );
        }
        else
            PushIllegalArgument();
    }
}


void ScInterpreter::ScConvert()
{   // Value, FromUnit, ToUnit
    if ( MustHaveParamCount( GetByte(), 3 ) )
    {
        String aToUnit( GetString() );
        String aFromUnit( GetString() );
        double fVal = GetDouble();
        if ( nGlobalError )
            PushError( nGlobalError);
        else
        {   // erst die angegebene Reihenfolge suchen, wenn nicht gefunden den Kehrwert
            double fConv;
            if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) )
                PushDouble( fVal * fConv );
            else if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) )
                PushDouble( fVal / fConv );
            else
                PushNA();
        }
    }
}


void ScInterpreter::ScRoman()
{   // Value [Mode]
    sal_uInt8 nParamCount = GetByte();
    if( MustHaveParamCount( nParamCount, 1, 2 ) )
    {
        double fMode = (nParamCount == 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0;
        double fVal = ::rtl::math::approxFloor( GetDouble() );
        if( nGlobalError )
            PushError( nGlobalError);
        else if( (fMode >= 0.0) && (fMode < 5.0) && (fVal >= 0.0) && (fVal < 4000.0) )
        {
            static const sal_Unicode pChars[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
            static const sal_uInt16 pValues[] = { 1000, 500, 100, 50, 10, 5, 1 };
            static const sal_uInt16 nMaxIndex = (sal_uInt16)(SAL_N_ELEMENTS(pValues) - 1);

            String aRoman;
            sal_uInt16 nVal = (sal_uInt16) fVal;
            sal_uInt16 nMode = (sal_uInt16) fMode;

            for( sal_uInt16 i = 0; i <= nMaxIndex / 2; i++ )
            {
                sal_uInt16 nIndex = 2 * i;
                sal_uInt16 nDigit = nVal / pValues[ nIndex ];

                if( (nDigit % 5) == 4 )
                {
                    sal_uInt16 nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2;
                    sal_uInt16 nSteps = 0;
                    while( (nSteps < nMode) && (nIndex < nMaxIndex) )
                    {
                        nSteps++;
                        if( pValues[ nIndex2 ] - pValues[ nIndex + 1 ] <= nVal )
                            nIndex++;
                        else
                            nSteps = nMode;
                    }
                    aRoman += pChars[ nIndex ];
                    aRoman += pChars[ nIndex2 ];
                    nVal = sal::static_int_cast<sal_uInt16>( nVal + pValues[ nIndex ] );
                    nVal = sal::static_int_cast<sal_uInt16>( nVal - pValues[ nIndex2 ] );
                }
                else
                {
                    if( nDigit > 4 )
                        aRoman += pChars[ nIndex - 1 ];
                    aRoman.Expand( aRoman.Len() + (nDigit % 5), pChars[ nIndex ] );
                    nVal %= pValues[ nIndex ];
                }
            }

            PushString( aRoman );
        }
        else
            PushIllegalArgument();
    }
}


sal_Bool lcl_GetArabicValue( sal_Unicode cChar, sal_uInt16& rnValue, sal_Bool& rbIsDec )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBase" );
    switch( cChar )
    {
        case 'M':   rnValue = 1000; rbIsDec = sal_True;     break;
        case 'D':   rnValue = 500;  rbIsDec = false;    break;
        case 'C':   rnValue = 100;  rbIsDec = sal_True;     break;
        case 'L':   rnValue = 50;   rbIsDec = false;    break;
        case 'X':   rnValue = 10;   rbIsDec = sal_True;     break;
        case 'V':   rnValue = 5;    rbIsDec = false;    break;
        case 'I':   rnValue = 1;    rbIsDec = sal_True;     break;
        default:    return false;
    }
    return sal_True;
}


void ScInterpreter::ScArabic()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArabic" );
    String aRoman( GetString() );
    if( nGlobalError )
        PushError( nGlobalError);
    else
    {
        aRoman.ToUpperAscii();

        sal_uInt16 nValue = 0;
        sal_uInt16 nValidRest = 3999;
        sal_uInt16 nCharIndex = 0;
        sal_uInt16 nCharCount = aRoman.Len();
        sal_Bool bValid = sal_True;

        while( bValid && (nCharIndex < nCharCount) )
        {
            sal_uInt16 nDigit1 = 0;
            sal_uInt16 nDigit2 = 0;
            sal_Bool bIsDec1 = false;
            sal_Bool bIsDec2 = false;
            bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex ), nDigit1, bIsDec1 );
            if( bValid && (nCharIndex + 1 < nCharCount) )
                bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex + 1 ), nDigit2, bIsDec2 );
            if( bValid )
            {
                if( nDigit1 >= nDigit2 )
                {
                    nValue = sal::static_int_cast<sal_uInt16>( nValue + nDigit1 );
                    nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2));
                    bValid = (nValidRest >= nDigit1);
                    if( bValid )
                        nValidRest = sal::static_int_cast<sal_uInt16>( nValidRest - nDigit1 );
                    nCharIndex++;
                }
                else if( nDigit1 * 2 != nDigit2 )
                {
                    sal_uInt16 nDiff = nDigit2 - nDigit1;
                    nValue = sal::static_int_cast<sal_uInt16>( nValue + nDiff );
                    bValid = (nValidRest >= nDiff);
                    if( bValid )
                        nValidRest = nDigit1 - 1;
                    nCharIndex += 2;
                }
                else
                    bValid = false;
            }
        }
        if( bValid )
            PushInt( nValue );
        else
            PushIllegalArgument();
    }
}


void ScInterpreter::ScHyperLink()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHyperLink" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    {
        double fVal = 0.0;
        String aStr;
        ScMatValType nResultType = SC_MATVAL_STRING;

        if ( nParamCount == 2 )
        {
            switch ( GetStackType() )
            {
                case svDouble:
                    fVal = GetDouble();
                    nResultType = SC_MATVAL_VALUE;
                break;
                case svString:
                    aStr = GetString();
                break;
                case svSingleRef:
                case svDoubleRef:
                {
                    ScAddress aAdr;
                    if ( !PopDoubleRefOrSingleRef( aAdr ) )
                        break;
                    ScBaseCell* pCell = GetCell( aAdr );
                    if (HasCellEmptyData( pCell))
                        nResultType = SC_MATVAL_EMPTY;
                    else
                    {
                        sal_uInt16 nErr = GetCellErrCode( pCell );
                        if (nErr)
                            SetError( nErr);
                        else if (HasCellValueData( pCell))
                        {
                            fVal = GetCellValue( aAdr, pCell );
                            nResultType = SC_MATVAL_VALUE;
                        }
                        else
                            GetCellString( aStr, pCell );
                    }
                }
                break;
                case svMatrix:
                    nResultType = GetDoubleOrStringFromMatrix( fVal, aStr);
                break;
                case svMissing:
                case svEmptyCell:
                    Pop();
                    // mimic xcl
                    fVal = 0.0;
                    nResultType = SC_MATVAL_VALUE;
                break;
                default:
                    PopError();
                    SetError( errIllegalArgument);
            }
        }
        String aUrl = GetString();
        ScMatrixRef pResMat = GetNewMat( 1, 2);
        if (nGlobalError)
        {
            fVal = CreateDoubleError( nGlobalError);
            nResultType = SC_MATVAL_VALUE;
        }
        if (nParamCount == 2 || nGlobalError)
        {
            if (ScMatrix::IsValueType( nResultType))
                pResMat->PutDouble( fVal, 0);
            else if (ScMatrix::IsRealStringType( nResultType))
                pResMat->PutString( aStr, 0);
            else    // EmptyType, EmptyPathType, mimic xcl
                pResMat->PutDouble( 0.0, 0 );
        }
        else
            pResMat->PutString( aUrl, 0 );
        pResMat->PutString( aUrl, 1 );
        bMatrixFormula = true;
        PushMatrix(pResMat);
    }
}


sal_Bool lclConvertMoney( const String& aSearchUnit, double& rfRate, int& rnDec )
{
    struct ConvertInfo
    {
        const sal_Char* pCurrText;
        double          fRate;
        int             nDec;
    };
    ConvertInfo aConvertTable[] = {
        { "EUR", 1.0,      2 },
        { "ATS", 13.7603,  2 },
        { "BEF", 40.3399,  0 },
        { "DEM", 1.95583,  2 },
        { "ESP", 166.386,  0 },
        { "FIM", 5.94573,  2 },
        { "FRF", 6.55957,  2 },
        { "IEP", 0.787564, 2 },
        { "ITL", 1936.27,  0 },
        { "LUF", 40.3399,  0 },
        { "NLG", 2.20371,  2 },
        { "PTE", 200.482,  2 },
        { "GRD", 340.750,  2 },
        { "SIT", 239.640,  2 },
        { "MTL", 0.429300, 2 },
        { "CYP", 0.585274, 2 },
        { "SKK", 30.1260,  2 }
    };

    const size_t nConversionCount = SAL_N_ELEMENTS(aConvertTable);
    for ( size_t i = 0; i < nConversionCount; i++ )
        if ( aSearchUnit.EqualsIgnoreCaseAscii( aConvertTable[i].pCurrText ) )
        {
            rfRate = aConvertTable[i].fRate;
            rnDec  = aConvertTable[i].nDec;
            return sal_True;
        }
    return false;
}

void ScInterpreter::ScEuroConvert()
{   //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]]
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 3, 5 ) )
    {
        double nPrecision = 0.0;
        if ( nParamCount == 5 )
        {
            nPrecision = ::rtl::math::approxFloor(GetDouble());
            if ( nPrecision < 3 )
            {
                PushIllegalArgument();
                return;
            }
        }
        sal_Bool bFullPrecision = false;
        if ( nParamCount >= 4 )
            bFullPrecision = GetBool();
        String aToUnit( GetString() );
        String aFromUnit( GetString() );
        double fVal = GetDouble();
        if ( nGlobalError )
            PushError( nGlobalError);
        else
        {
            double fFromRate;
            double fToRate;
            int    nFromDec;
            int    nToDec;
            String aEur( RTL_CONSTASCII_USTRINGPARAM("EUR"));
            if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec )
                && lclConvertMoney( aToUnit, fToRate, nToDec ) )
            {
                double fRes;
                if ( aFromUnit.EqualsIgnoreCaseAscii( aToUnit ) )
                    fRes = fVal;
                else
                {
                    if ( aFromUnit.EqualsIgnoreCaseAscii( aEur ) )
                       fRes = fVal * fToRate;
                    else
                    {
                        double fIntermediate = fVal / fFromRate;
                        if ( nPrecision )
                            fIntermediate = ::rtl::math::round( fIntermediate,
                                                            (int) nPrecision );
                        fRes = fIntermediate * fToRate;
                    }
                    if ( !bFullPrecision )
                        fRes = ::rtl::math::round( fRes, nToDec );
                }
                PushDouble( fRes );
            }
            else
                PushIllegalArgument();
        }
    }
}


// BAHTTEXT ===================================================================

#define UTF8_TH_0       "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
#define UTF8_TH_1       "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
#define UTF8_TH_2       "\340\270\252\340\270\255\340\270\207"
#define UTF8_TH_3       "\340\270\252\340\270\262\340\270\241"
#define UTF8_TH_4       "\340\270\252\340\270\265\340\271\210"
#define UTF8_TH_5       "\340\270\253\340\271\211\340\270\262"
#define UTF8_TH_6       "\340\270\253\340\270\201"
#define UTF8_TH_7       "\340\271\200\340\270\210\340\271\207\340\270\224"
#define UTF8_TH_8       "\340\271\201\340\270\233\340\270\224"
#define UTF8_TH_9       "\340\271\200\340\270\201\340\271\211\340\270\262"
#define UTF8_TH_10      "\340\270\252\340\270\264\340\270\232"
#define UTF8_TH_11      "\340\271\200\340\270\255\340\271\207\340\270\224"
#define UTF8_TH_20      "\340\270\242\340\270\265\340\271\210"
#define UTF8_TH_1E2     "\340\270\243\340\271\211\340\270\255\340\270\242"
#define UTF8_TH_1E3     "\340\270\236\340\270\261\340\270\231"
#define UTF8_TH_1E4     "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
#define UTF8_TH_1E5     "\340\271\201\340\270\252\340\270\231"
#define UTF8_TH_1E6     "\340\270\245\340\271\211\340\270\262\340\270\231"
#define UTF8_TH_DOT0    "\340\270\226\340\271\211\340\270\247\340\270\231"
#define UTF8_TH_BAHT    "\340\270\232\340\270\262\340\270\227"
#define UTF8_TH_SATANG  "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
#define UTF8_TH_MINUS   "\340\270\245\340\270\232"

#define UTF8_STRINGPARAM( ascii )   ascii, static_cast< xub_StrLen >( sizeof( ascii ) - 1 )
#define UTF8_CREATE( ascii )        ByteString( UTF8_STRINGPARAM( ascii ) )
#define UTF8_APPEND( ascii )        Append( UTF8_STRINGPARAM( ascii ) )
#define UTF8_PREPEND( ascii )       Insert( UTF8_CREATE( ascii ), 0 )

// local functions ------------------------------------------------------------

namespace {

inline void lclSplitBlock( double& rfInt, sal_Int32& rnBlock, double fValue, double fSize )
{
    rnBlock = static_cast< sal_Int32 >( modf( (fValue + 0.1) / fSize, &rfInt ) * fSize + 0.1 );
}

/** Appends a digit (0 to 9) to the passed string. */
void lclAppendDigit( ByteString& rText, sal_Int32 nDigit )
{
    switch( nDigit )
    {
        case 0: rText.UTF8_APPEND( UTF8_TH_0 ); break;
        case 1: rText.UTF8_APPEND( UTF8_TH_1 ); break;
        case 2: rText.UTF8_APPEND( UTF8_TH_2 ); break;
        case 3: rText.UTF8_APPEND( UTF8_TH_3 ); break;
        case 4: rText.UTF8_APPEND( UTF8_TH_4 ); break;
        case 5: rText.UTF8_APPEND( UTF8_TH_5 ); break;
        case 6: rText.UTF8_APPEND( UTF8_TH_6 ); break;
        case 7: rText.UTF8_APPEND( UTF8_TH_7 ); break;
        case 8: rText.UTF8_APPEND( UTF8_TH_8 ); break;
        case 9: rText.UTF8_APPEND( UTF8_TH_9 ); break;
        default:    DBG_ERRORFILE( "lclAppendDigit - illegal digit" );
    }
}

/** Appends a value raised to a power of 10: nDigit*10^nPow10.
    @param nDigit  A digit in the range from 1 to 9.
    @param nPow10  A value in the range from 2 to 5.
 */
void lclAppendPow10( ByteString& rText, sal_Int32 nDigit, sal_Int32 nPow10 )
{
    DBG_ASSERT( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" );
    lclAppendDigit( rText, nDigit );
    switch( nPow10 )
    {
        case 2: rText.UTF8_APPEND( UTF8_TH_1E2 );   break;
        case 3: rText.UTF8_APPEND( UTF8_TH_1E3 );   break;
        case 4: rText.UTF8_APPEND( UTF8_TH_1E4 );   break;
        case 5: rText.UTF8_APPEND( UTF8_TH_1E5 );   break;
        default:    DBG_ERRORFILE( "lclAppendPow10 - illegal power" );
    }
}

/** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
void lclAppendBlock( ByteString& rText, sal_Int32 nValue )
{
    DBG_ASSERT( (1 <= nValue) && (nValue <= 999999), "lclAppendBlock - illegal value" );
    if( nValue >= 100000 )
    {
        lclAppendPow10( rText, nValue / 100000, 5 );
        nValue %= 100000;
    }
    if( nValue >= 10000 )
    {
        lclAppendPow10( rText, nValue / 10000, 4 );
        nValue %= 10000;
    }
    if( nValue >= 1000 )
    {
        lclAppendPow10( rText, nValue / 1000, 3 );
        nValue %= 1000;
    }
    if( nValue >= 100 )
    {
        lclAppendPow10( rText, nValue / 100, 2 );
        nValue %= 100;
    }
    if( nValue > 0 )
    {
        sal_Int32 nTen = nValue / 10;
        sal_Int32 nOne = nValue % 10;
        if( nTen >= 1 )
        {
            if( nTen >= 3 )
                lclAppendDigit( rText, nTen );
            else if( nTen == 2 )
                rText.UTF8_APPEND( UTF8_TH_20 );
            rText.UTF8_APPEND( UTF8_TH_10 );
        }
        if( (nTen > 0) && (nOne == 1) )
            rText.UTF8_APPEND( UTF8_TH_11 );
        else if( nOne > 0 )
            lclAppendDigit( rText, nOne );
    }
}

} // namespace

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

void ScInterpreter::ScBahtText()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBahtText" );
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCount( nParamCount, 1 ) )
    {
        double fValue = GetDouble();
        if( nGlobalError )
        {
            PushError( nGlobalError);
            return;
        }

        // sign
        bool bMinus = fValue < 0.0;
        fValue = fabs( fValue );

        // round to 2 digits after decimal point, fValue contains Satang as integer
        fValue = ::rtl::math::approxFloor( fValue * 100.0 + 0.5 );

        // split Baht and Satang
        double fBaht = 0.0;
        sal_Int32 nSatang = 0;
        lclSplitBlock( fBaht, nSatang, fValue, 100.0 );

        ByteString aText;

        // generate text for Baht value
        if( fBaht == 0.0 )
        {
            if( nSatang == 0 )
                aText.UTF8_APPEND( UTF8_TH_0 );
        }
        else while( fBaht > 0.0 )
        {
            ByteString aBlock;
            sal_Int32 nBlock = 0;
            lclSplitBlock( fBaht, nBlock, fBaht, 1.0e6 );
            if( nBlock > 0 )
                lclAppendBlock( aBlock, nBlock );
            // add leading "million", if there will come more blocks
            if( fBaht > 0.0 )
                aBlock.UTF8_PREPEND( UTF8_TH_1E6 );
            aText.Insert( aBlock, 0 );
        }
        if( aText.Len() > 0 )
            aText.UTF8_APPEND( UTF8_TH_BAHT );

        // generate text for Satang value
        if( nSatang == 0 )
        {
            aText.UTF8_APPEND( UTF8_TH_DOT0 );
        }
        else
        {
            lclAppendBlock( aText, nSatang );
            aText.UTF8_APPEND( UTF8_TH_SATANG );
        }

        // add the minus sign
        if( bMinus )
            aText.UTF8_PREPEND( UTF8_TH_MINUS );

        PushString( String( aText, RTL_TEXTENCODING_UTF8 ) );
    }
}

// ============================================================================

void ScInterpreter::ScGetPivotData()
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetPivotData" );
    sal_uInt8 nParamCount = GetByte();

    if ( MustHaveParamCount( nParamCount, 2, 30 ) )
    {
        // there must be an even number of args
        //      target, ref, then field/item pairs
        if( (nParamCount % 2) == 1)
            goto failed;

        bool bOldSyntax = false;
        if ( nParamCount == 2 )
        {
            // if the first parameter is a ref, assume old syntax
            StackVar eFirstType = GetStackType( 2 );
            if ( eFirstType == svSingleRef || eFirstType == svDoubleRef )
                bOldSyntax = true;
        }

        ScDPGetPivotDataField aTarget;                  // target field, and returns result
        std::vector< ScDPGetPivotDataField > aFilters;
        String aFilterList;
        if ( bOldSyntax )
            aFilterList = GetString();      // old syntax: second parameter is list of constraints
        else
        {
            // new syntax: separate name/value pairs

            sal_uInt16 nFilterCount = nParamCount / 2 - 1;
            aFilters.resize( nFilterCount );

            sal_uInt16 i = nFilterCount;
            while( i-- > 0 )
            {
                //! should allow numeric constraint values
                aFilters[i].mbValIsStr = sal_True;
                aFilters[i].maValStr = GetString();

                aFilters[i].maFieldName = GetString();
            }
        }

        // common to both syntaxes: a reference to the data pilot table

        ScRange aBlock;
        switch ( GetStackType() )
        {
            case svDoubleRef :
                PopDoubleRef( aBlock );
                break;

            case svSingleRef :
                {
                    ScAddress aAddr;
                    PopSingleRef( aAddr );
                    aBlock = aAddr;
                    break;
                }
            default:
                goto failed;
        }
        // NOTE : MS Excel docs claim to use the 'most recent' which is not
        // exactly the same as what we do in ScDocument::GetDPAtBlock
        // However we do need to use GetDPABlock
        ScDPObject* pDPObj = pDok->GetDPAtBlock ( aBlock );
        if( NULL == pDPObj)
            goto failed;

        if ( bOldSyntax )
        {
            // fill aFilters / aTarget from aFilterList string
            if ( !pDPObj->ParseFilters( aTarget, aFilters, aFilterList ) )
                goto failed;
        }
        else
            aTarget.maFieldName = GetString();      // new syntax: first parameter is data field name

        if( pDPObj->GetPivotData( aTarget, aFilters ) )
        {
            if( aTarget.mbValIsStr )
                PushString( aTarget.maValStr );
            else
                PushDouble( aTarget.mnValNum );
            return;
        }
    }

failed :
    PushError( errNoRef );
}

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