diff options
Diffstat (limited to 'sc/source/core/tool/interpr2.cxx')
-rw-r--r-- | sc/source/core/tool/interpr2.cxx | 3035 |
1 files changed, 3035 insertions, 0 deletions
diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx new file mode 100644 index 000000000000..fc0f085706c5 --- /dev/null +++ b/sc/source/core/tool/interpr2.cxx @@ -0,0 +1,3035 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: interpr2.cxx,v $ + * $Revision: 1.37.88.3 $ + * + * 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 <svx/linkmgr.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <svtools/stritem.hxx> +#include <svtools/zforlist.hxx> +#include <rtl/logfile.hxx> + +#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( INT16 nYear, INT16 nMonth, 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. + 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" ); + BYTE 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 ) ) + { + INT16 nDay, nMonth, nYear; + nYear = (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<INT16>( O % 31 + 1 ); + nMonth = sal::static_int_cast<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 ) ) + { + INT16 nDay = (INT16) ::rtl::math::approxFloor(GetDouble()); + INT16 nMonth = (INT16) ::rtl::math::approxFloor(GetDouble()); + INT16 nYear = (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. + */ + + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + BOOL bFlag; + if (nParamCount == 3) + bFlag = GetBool(); + else + bFlag = FALSE; + double nDate2 = GetDouble(); + double nDate1 = GetDouble(); + double fSign; + if (nGlobalError) + PushError( nGlobalError); + else + { + // #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 -= (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 -= (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" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + double fVal = 0.0; + if (nParamCount == 1) + fVal = ::rtl::math::round( GetDouble(), 0, eMode ); + else + { + INT32 nDec = (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" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + 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" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + 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" ); + BYTE 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 : + { + USHORT 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); + } +} + +#if defined(WIN) && defined(MSC) +#pragma optimize("",off) +#endif + +void ScInterpreter::ScIRR() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIRR" ); + double fSchaetzwert; + nFuncFmtType = NUMBERFORMAT_PERCENT; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) + return; + if (nParamCount == 2) + fSchaetzwert = GetDouble(); + else + fSchaetzwert = 0.1; + USHORT 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 USHORT nIterationsMax = 20; + USHORT nItCount = 0; + ScRange aRange; + while (fEps > SCdEpsilon && nItCount < nIterationsMax) + { // Newton-Verfahren: + sp = sPos; // Stack zuruecksetzen + nCount = 0.0; + fZaehler = 0.0; + fNenner = 0.0; + USHORT 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); +} +#if defined(WIN) && defined(MSC) +#pragma optimize("",on) +#endif + + +void ScInterpreter::ScMIRR() +{ // range_of_values ; rate_invest ; rate_reinvest + nFuncFmtType = NUMBERFORMAT_PERCENT; + if( MustHaveParamCount( GetByte(), 3 ) ) + { + double fRate1_reinvest = GetDouble() + 1; + double fNPV_reinvest = 0.0; + double fPow_reinvest = 1.0; + + double fRate1_invest = GetDouble() + 1; + double fNPV_invest = 0.0; + double fPow_invest = 1.0; + + ScRange aRange; + PopDoubleRef( aRange ); + + if( nGlobalError ) + PushError( nGlobalError); + else + { + ScValueIterator aValIter( pDok, aRange, glSubTotal ); + double fCellValue; + ULONG nCount = 0; + USHORT nIterError = 0; + + 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; + BYTE 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; + BYTE 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; + BYTE 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; + USHORT iMax = (USHORT)::rtl::math::approxFloor(nMin); + for (USHORT 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); + ULONG nLoopEnd = (ULONG) fIntEnd; + + double fTerm, fLia; + double fRestwert = fWert - fRest; + BOOL bNowLia = FALSE; + + double fGda; + 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 = 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; + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 5, 7 ) ) + { + double fWert, fRest, fDauer, fAnfang, fEnde, fFaktor, fVdb = 0.0; + 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); + ULONG nLoopStart = (ULONG) fIntStart; + ULONG nLoopEnd = (ULONG) fIntEnd; + + fVdb = 0.0; + if (bFlag) + { + for (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; + double fPart; + + //@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)) + { + 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; + BYTE 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; + BYTE 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; + BYTE 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 USHORT nIterationsMax = 150; + USHORT 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; + BYTE 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, nRmz, nZzr, nBw, nZw = 0, nFlag = 0; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE 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 + 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, nRmz, nZinsz; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE 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 + { + 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, fRmz, fZinsZ; + 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 + { + ULONG nAnfang = (ULONG) fAnfang; + ULONG nEnde = (ULONG) fEnde ; + fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF); + fZinsZ = 0.0; + if (nAnfang == 1) + { + if (fF <= 0.0) + fZinsZ = -fBw; + nAnfang++; + } + for (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, fRmz, fKapZ; + 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 + { + fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF); + fKapZ = 0.0; + ULONG nAnfang = (ULONG) fAnfang; + ULONG nEnde = (ULONG) fEnde; + if (nAnfang == 1) + { + if (fF <= 0.0) + fKapZ = fRmz + fBw * fZins; + else + fKapZ = fRmz; + nAnfang++; + } + for (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 ) ) + { + 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 + 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 USHORT 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(); + BOOL bError = ( pFormula->GetErrCode() != 0 ); + // bError always corresponds with fF + + fFPrev = pFormula->GetValue() - fTargetVal; + + fBestF = fabs( fFPrev ); + if ( fBestF < fDelta ) + bDoneIteration = TRUE; + + double fX = fXPrev + fEps; + double fF = fFPrev; + double fSlope; + + USHORT nIter = 0; + + 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# + + USHORT nHorIter = 0; + const double fHorStepAngle = 5.0; + const double fHorMaxAngle = 80.0; + int nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle ); + BOOL bDoneHorMove = FALSE; + + while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter ) + { + double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter ); + double fHorTangent = ::rtl::math::tan( fHorAngle * F_PI / 180 ); + + USHORT 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 = TRUE; + } + } + } + if ( !bDoneHorMove ) + bHorMoveError = 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 = 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; +// double nX = ::rtl::math::approxFloor((fBestX / fDelta) + 0.5) * fDelta; + + 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); + } + 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); +} + + +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); +} + + +void ScInterpreter::ScCurrent() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCurrent" ); + FormulaTokenRef xTok( PopToken()); + if (xTok) + { + PushTempToken( xTok); + PushTempToken( xTok); + } + else + PushError( errUnknownStackVariable); +} + +void ScInterpreter::ScStyle() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStyle" ); + BYTE 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( SvxLinkManager* pLinkMgr, + const String& rA, const String& rT, const String& rI, BYTE nM ) +{ + USHORT nCount = pLinkMgr->GetLinks().Count(); + for (USHORT 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 + + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + BYTE nMode = SC_DDE_DEFAULT; + if (nParamCount == 4) + nMode = (BYTE) ::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 + + SvxLinkManager* 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) + + BOOL bOldDis = pDok->IsIdleDisabled(); + pDok->DisableIdle( 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 ); + + 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 ); + } +} + +void ScInterpreter::ScBase() +{ // Value, Base [, MinLen] + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + static const sal_Unicode __FAR_DATA 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 = (sizeof(pDigits)/sizeof(sal_Unicode))-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 <= (ULONG)(~0) ) + { + ULONG nVal = (ULONG) fVal; + ULONG nBase = (ULONG) fBase; + while ( nVal && p > pBuf ) + { + *--p = pDigits[ nVal % nBase ]; + nVal /= nBase; + } + fVal = (double) nVal; + } + else + { + 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 + // #53943# =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 = 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] + BYTE 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 USHORT pValues[] = { 1000, 500, 100, 50, 10, 5, 1 }; + static const USHORT nMaxIndex = (USHORT)(sizeof(pValues) / sizeof(pValues[0]) - 1); + + String aRoman; + USHORT nVal = (USHORT) fVal; + USHORT nMode = (USHORT) fMode; + + for( UINT16 i = 0; i <= nMaxIndex / 2; i++ ) + { + USHORT nIndex = 2 * i; + USHORT nDigit = nVal / pValues[ nIndex ]; + + if( (nDigit % 5) == 4 ) + { + USHORT nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2; + USHORT 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<USHORT>( nVal + pValues[ nIndex ] ); + nVal = sal::static_int_cast<USHORT>( 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(); + } +} + + +BOOL lcl_GetArabicValue( sal_Unicode cChar, USHORT& rnValue, BOOL& rbIsDec ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBase" ); + switch( cChar ) + { + case 'M': rnValue = 1000; rbIsDec = TRUE; break; + case 'D': rnValue = 500; rbIsDec = FALSE; break; + case 'C': rnValue = 100; rbIsDec = TRUE; break; + case 'L': rnValue = 50; rbIsDec = FALSE; break; + case 'X': rnValue = 10; rbIsDec = TRUE; break; + case 'V': rnValue = 5; rbIsDec = FALSE; break; + case 'I': rnValue = 1; rbIsDec = TRUE; break; + default: return FALSE; + } + return TRUE; +} + + +void ScInterpreter::ScArabic() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArabic" ); + String aRoman( GetString() ); + if( nGlobalError ) + PushError( nGlobalError); + else + { + aRoman.ToUpperAscii(); + + USHORT nValue = 0; + USHORT nValidRest = 3999; + USHORT nCharIndex = 0; + USHORT nCharCount = aRoman.Len(); + BOOL bValid = TRUE; + + while( bValid && (nCharIndex < nCharCount) ) + { + USHORT nDigit1 = 0; + USHORT nDigit2 = 0; + BOOL bIsDec1 = FALSE; + 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<USHORT>( nValue + nDigit1 ); + nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2)); + bValid = (nValidRest >= nDigit1); + if( bValid ) + nValidRest = sal::static_int_cast<USHORT>( nValidRest - nDigit1 ); + nCharIndex++; + } + else if( nDigit1 * 2 != nDigit2 ) + { + USHORT nDiff = nDigit2 - nDigit1; + nValue = sal::static_int_cast<USHORT>( 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" ); + BYTE 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 + { + USHORT 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); + } +} + + +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 = sizeof( aConvertTable ) / sizeof( aConvertTable[0] ); + for ( size_t i = 0; i < nConversionCount; i++ ) + if ( aSearchUnit.EqualsIgnoreCaseAscii( aConvertTable[i].pCurrText ) ) + { + rfRate = aConvertTable[i].fRate; + rnDec = aConvertTable[i].nDec; + return TRUE; + } + return FALSE; +} + +void ScInterpreter::ScEuroConvert() +{ //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]] + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 5 ) ) + { + double nPrecision = 0.0; + if ( nParamCount == 5 ) + { + nPrecision = ::rtl::math::approxFloor(GetDouble()); + if ( nPrecision < 3 ) + { + PushIllegalArgument(); + return; + } + } + BOOL bFullPrecision = FALSE; + if ( nParamCount >= 4 ) + bFullPrecision = GetBool(); + String aToUnit( GetString() ); + String aFromUnit( GetString() ); + double fVal = GetDouble(); + if ( nGlobalError ) + PushError( nGlobalError); + else + { + double fRes; + double fFromRate; + double fToRate; + int nFromDec; + int nToDec; + String aEur( RTL_CONSTASCII_USTRINGPARAM("EUR")); + if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec ) + && lclConvertMoney( aToUnit, fToRate, nToDec ) ) + { + 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" ); + BYTE 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" ); + BYTE 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 + + USHORT nFilterCount = nParamCount / 2 - 1; + aFilters.resize( nFilterCount ); + + USHORT i = nFilterCount; + while( i-- > 0 ) + { + //! should allow numeric constraint values + aFilters[i].mbValIsStr = 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 ); +} + |