update dev300-m57
[ooovba.git] / sc / source / core / tool / interpr2.cxx
blobb4949fa61ace40cbaf5770a83186839f01c79772
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: interpr2.cxx,v $
10 * $Revision: 1.37.88.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 // INCLUDE ---------------------------------------------------------------
36 #include <svx/linkmgr.hxx>
37 #include <sfx2/dispatch.hxx>
38 #include <sfx2/objsh.hxx>
39 #include <svtools/stritem.hxx>
40 #include <svtools/zforlist.hxx>
41 #include <rtl/logfile.hxx>
43 #include "interpre.hxx"
44 #include "attrib.hxx"
45 #include "sc.hrc"
46 #include "ddelink.hxx"
47 #include "scmatrix.hxx"
48 #include "compiler.hxx"
49 #include "cell.hxx"
50 #include "document.hxx"
51 #include "dociter.hxx"
52 #include "docoptio.hxx"
53 #include "unitconv.hxx"
54 #include "globstr.hrc"
55 #include "hints.hxx"
56 #include "dpobject.hxx"
57 #include "postit.hxx"
59 #include <string.h>
60 #include <math.h>
62 using namespace formula;
63 // STATIC DATA -----------------------------------------------------------
65 #define D_TIMEFACTOR 86400.0
66 #define SCdEpsilon 1.0E-7
68 //-----------------------------------------------------------------------------
69 // Datum und Zeit
70 //-----------------------------------------------------------------------------
72 double ScInterpreter::GetDate(INT16 nYear, INT16 nMonth, INT16 nDay)
74 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::GetDate" );
75 if ( nYear < 100 )
76 nYear = pFormatter->ExpandTwoDigitYear( nYear );
77 INT16 nY, nM;
78 if (nMonth > 0)
80 nY = nYear + (nMonth-1) / 12;
81 nM = ((nMonth-1) % 12) + 1;
83 else
85 nY = nYear + (nMonth-12) / 12;
86 nM = 12 - (-nMonth) % 12;
88 Date aDate(1, nM, nY);
89 aDate += nDay - 1;
90 if (aDate.IsValid())
91 return (double) (aDate - *(pFormatter->GetNullDate()));
92 else
94 SetError(errNoValue);
95 return 0;
99 //-----------------------------------------------------------------------------
100 // Funktionen
101 //-----------------------------------------------------------------------------
103 void ScInterpreter::ScGetActDate()
105 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetActDate" );
106 nFuncFmtType = NUMBERFORMAT_DATE;
107 Date aActDate;
108 long nDiff = aActDate - *(pFormatter->GetNullDate());
109 PushDouble((double) nDiff);
112 void ScInterpreter::ScGetActTime()
114 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetActTime" );
115 nFuncFmtType = NUMBERFORMAT_DATETIME;
116 Date aActDate;
117 long nDiff = aActDate - *(pFormatter->GetNullDate());
118 Time aActTime;
119 double nTime = ((double)aActTime.Get100Sec() / 100 +
120 (double)(aActTime.GetSec() +
121 (aActTime.GetMin() * 60) +
122 (aActTime.GetHour() * 3600))) / D_TIMEFACTOR;
123 PushDouble( (double) nDiff + nTime );
126 void ScInterpreter::ScGetYear()
128 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetYear" );
129 Date aDate = *(pFormatter->GetNullDate());
130 aDate += (long) ::rtl::math::approxFloor(GetDouble());
131 PushDouble( (double) aDate.GetYear() );
134 void ScInterpreter::ScGetMonth()
136 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetMonth" );
137 Date aDate = *(pFormatter->GetNullDate());
138 aDate += (long) ::rtl::math::approxFloor(GetDouble());
139 PushDouble( (double) aDate.GetMonth() );
142 void ScInterpreter::ScGetDay()
144 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDay" );
145 Date aDate = *(pFormatter->GetNullDate());
146 aDate += (long)::rtl::math::approxFloor(GetDouble());
147 PushDouble((double) aDate.GetDay());
150 void ScInterpreter::ScGetMin()
152 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetMin" );
153 double fTime = GetDouble();
154 fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg
155 long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 3600;
156 PushDouble( (double) (nVal/60) );
159 void ScInterpreter::ScGetSec()
161 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetSec" );
162 double fTime = GetDouble();
163 fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg
164 long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 60;
165 PushDouble( (double) nVal );
168 void ScInterpreter::ScGetHour()
170 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetHour" );
171 double fTime = GetDouble();
172 fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg
173 long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) / 3600;
174 PushDouble((double) nVal);
177 void ScInterpreter::ScGetDateValue()
179 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDateValue" );
180 String aInputString = GetString();
181 sal_uInt32 nFIndex = 0; // damit default Land/Spr.
182 double fVal;
183 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
185 short eType = pFormatter->GetType(nFIndex);
186 if (eType == NUMBERFORMAT_DATE || eType == NUMBERFORMAT_DATETIME)
187 PushDouble(::rtl::math::approxFloor(fVal));
188 else
189 PushIllegalArgument();
191 else
192 PushIllegalArgument();
195 void ScInterpreter::ScGetDayOfWeek()
197 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDayOfWeek" );
198 BYTE nParamCount = GetByte();
199 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
201 short nFlag;
202 if (nParamCount == 2)
203 nFlag = (short) ::rtl::math::approxFloor(GetDouble());
204 else
205 nFlag = 1;
207 Date aDate = *(pFormatter->GetNullDate());
208 aDate += (long)::rtl::math::approxFloor(GetDouble());
209 int nVal = (int) aDate.GetDayOfWeek();
210 if (nFlag == 1)
212 if (nVal == 6)
213 nVal = 1;
214 else
215 nVal += 2;
217 else if (nFlag == 2)
218 nVal += 1;
219 PushInt( nVal );
223 void ScInterpreter::ScGetWeekOfYear()
225 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetWeekOfYear" );
226 if ( MustHaveParamCount( GetByte(), 2 ) )
228 short nFlag = (short) ::rtl::math::approxFloor(GetDouble());
230 Date aDate = *(pFormatter->GetNullDate());
231 aDate += (long)::rtl::math::approxFloor(GetDouble());
232 PushInt( (int) aDate.GetWeekOfYear( nFlag == 1 ? SUNDAY : MONDAY ));
236 void ScInterpreter::ScEasterSunday()
238 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScEasterSunday" );
239 nFuncFmtType = NUMBERFORMAT_DATE;
240 if ( MustHaveParamCount( GetByte(), 1 ) )
242 INT16 nDay, nMonth, nYear;
243 nYear = (INT16) ::rtl::math::approxFloor( GetDouble() );
244 if ( nYear < 100 )
245 nYear = pFormatter->ExpandTwoDigitYear( nYear );
246 // don't worry, be happy :)
247 int B,C,D,E,F,G,H,I,K,L,M,N,O;
248 N = nYear % 19;
249 B = int(nYear / 100);
250 C = nYear % 100;
251 D = int(B / 4);
252 E = B % 4;
253 F = int((B + 8) / 25);
254 G = int((B - F + 1) / 3);
255 H = (19 * N + B - D - G + 15) % 30;
256 I = int(C / 4);
257 K = C % 4;
258 L = (32 + 2 * E + 2 * I - H - K) % 7;
259 M = int((N + 11 * H + 22 * L) / 451);
260 O = H + L - 7 * M + 114;
261 nDay = sal::static_int_cast<INT16>( O % 31 + 1 );
262 nMonth = sal::static_int_cast<INT16>( int(O / 31) );
263 PushDouble( GetDate( nYear, nMonth, nDay ) );
267 void ScInterpreter::ScGetDate()
269 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDate" );
270 nFuncFmtType = NUMBERFORMAT_DATE;
271 if ( MustHaveParamCount( GetByte(), 3 ) )
273 INT16 nDay = (INT16) ::rtl::math::approxFloor(GetDouble());
274 INT16 nMonth = (INT16) ::rtl::math::approxFloor(GetDouble());
275 INT16 nYear = (INT16) ::rtl::math::approxFloor(GetDouble());
276 if (nYear < 0)
277 PushIllegalArgument();
278 else
280 PushDouble(GetDate(nYear, nMonth, nDay));
285 void ScInterpreter::ScGetTime()
287 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetTime" );
288 nFuncFmtType = NUMBERFORMAT_TIME;
289 if ( MustHaveParamCount( GetByte(), 3 ) )
291 double nSec = GetDouble();
292 double nMin = GetDouble();
293 double nHour = GetDouble();
294 PushDouble( ( (nHour * 3600) + (nMin * 60) + nSec ) / D_TIMEFACTOR );
298 void ScInterpreter::ScGetDiffDate()
300 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDiffDate" );
301 if ( MustHaveParamCount( GetByte(), 2 ) )
303 double nDate2 = GetDouble();
304 double nDate1 = GetDouble();
305 PushDouble(nDate1 - nDate2);
309 void ScInterpreter::ScGetDiffDate360()
311 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetDiffDate360" );
312 /* Implementation follows
313 * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf
314 * Appendix B: Day-Count Bases, there are 7 different ways to calculate the
315 * 30-days count. That document also claims that Excel implements the "PSA
316 * 30" or "NASD 30" method (funny enough they also state that Excel is the
317 * only tool that does so).
319 * Note that the definiton given in
320 * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp
321 * is _not_ the way how it is actually calculated by Excel (that would not
322 * even match any of the 7 methods mentioned above) and would result in the
323 * following test cases producing wrong results according to that appendix B:
325 * 28-Feb-95 31-Aug-95 181 instead of 180
326 * 29-Feb-96 31-Aug-96 181 instead of 180
327 * 30-Jan-96 31-Mar-96 61 instead of 60
328 * 31-Jan-96 31-Mar-96 61 instead of 60
330 * Still, there is a difference between OOoCalc and Excel:
331 * In Excel:
332 * 02-Feb-99 31-Mar-00 results in 419
333 * 31-Mar-00 02-Feb-99 results in -418
334 * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
337 BYTE nParamCount = GetByte();
338 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
340 BOOL bFlag;
341 if (nParamCount == 3)
342 bFlag = GetBool();
343 else
344 bFlag = FALSE;
345 double nDate2 = GetDouble();
346 double nDate1 = GetDouble();
347 double fSign;
348 if (nGlobalError)
349 PushError( nGlobalError);
350 else
352 // #i84934# only for non-US European algorithm swap dates. Else
353 // follow Excel's meaningless extrapolation for "interoperability".
354 if (bFlag && (nDate2 < nDate1))
356 fSign = nDate1;
357 nDate1 = nDate2;
358 nDate2 = fSign;
359 fSign = -1.0;
361 else
362 fSign = 1.0;
363 Date aDate1 = *(pFormatter->GetNullDate());
364 aDate1 += (long) ::rtl::math::approxFloor(nDate1);
365 Date aDate2 = *(pFormatter->GetNullDate());
366 aDate2 += (long) ::rtl::math::approxFloor(nDate2);
367 if (aDate1.GetDay() == 31)
368 aDate1 -= (ULONG) 1;
369 else if (!bFlag)
371 if (aDate1.GetMonth() == 2)
373 switch ( aDate1.GetDay() )
375 case 28 :
376 if ( !aDate1.IsLeapYear() )
377 aDate1.SetDay(30);
378 break;
379 case 29 :
380 aDate1.SetDay(30);
381 break;
385 if (aDate2.GetDay() == 31)
387 if (!bFlag )
389 if (aDate1.GetDay() == 30)
390 aDate2 -= (ULONG) 1;
392 else
393 aDate2.SetDay(30);
395 PushDouble( fSign * (double)
396 ( (double) aDate2.GetDay() + (double) aDate2.GetMonth() * 30.0 +
397 (double) aDate2.GetYear() * 360.0
398 - (double) aDate1.GetDay() - (double) aDate1.GetMonth() * 30.0
399 - (double)aDate1.GetYear() * 360.0) );
404 void ScInterpreter::ScGetTimeValue()
406 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetTimeValue" );
407 String aInputString = GetString();
408 sal_uInt32 nFIndex = 0; // damit default Land/Spr.
409 double fVal;
410 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
412 short eType = pFormatter->GetType(nFIndex);
413 if (eType == NUMBERFORMAT_TIME || eType == NUMBERFORMAT_DATETIME)
415 double fDateVal = rtl::math::approxFloor(fVal);
416 double fTimeVal = fVal - fDateVal;
417 PushDouble(fTimeVal);
419 else
420 PushIllegalArgument();
422 else
423 PushIllegalArgument();
426 void ScInterpreter::ScPlusMinus()
428 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScPlusMinus" );
429 double nVal = GetDouble();
430 short n = 0;
431 if (nVal < 0.0)
432 n = -1;
433 else if (nVal > 0.0)
434 n = 1;
435 PushInt( n );
438 void ScInterpreter::ScAbs()
440 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAbs" );
441 PushDouble(fabs(GetDouble()));
444 void ScInterpreter::ScInt()
446 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScInt" );
447 PushDouble(::rtl::math::approxFloor(GetDouble()));
451 void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode )
453 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::RoundNumber" );
454 BYTE nParamCount = GetByte();
455 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
457 double fVal = 0.0;
458 if (nParamCount == 1)
459 fVal = ::rtl::math::round( GetDouble(), 0, eMode );
460 else
462 INT32 nDec = (INT32) ::rtl::math::approxFloor(GetDouble());
463 if( nDec < -20 || nDec > 20 )
464 PushIllegalArgument();
465 else
466 fVal = ::rtl::math::round( GetDouble(), (short)nDec, eMode );
468 PushDouble(fVal);
472 void ScInterpreter::ScRound()
474 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRound" );
475 RoundNumber( rtl_math_RoundingMode_Corrected );
478 void ScInterpreter::ScRoundDown()
480 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRoundDown" );
481 RoundNumber( rtl_math_RoundingMode_Down );
484 void ScInterpreter::ScRoundUp()
486 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRoundUp" );
487 RoundNumber( rtl_math_RoundingMode_Up );
490 void ScInterpreter::ScCeil()
492 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCeil" );
493 BYTE nParamCount = GetByte();
494 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
496 BOOL bAbs = ( nParamCount == 3 ? GetBool() : FALSE );
497 double fDec = GetDouble();
498 double fVal = GetDouble();
499 if ( fDec == 0.0 )
500 PushInt(0);
501 else if (fVal*fDec < 0.0)
502 PushIllegalArgument();
503 else
505 if ( !bAbs && fVal < 0.0 )
506 PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec);
507 else
508 PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
513 void ScInterpreter::ScFloor()
515 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScFloor" );
516 BYTE nParamCount = GetByte();
517 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
519 BOOL bAbs = ( nParamCount == 3 ? GetBool() : FALSE );
520 double fDec = GetDouble();
521 double fVal = GetDouble();
522 if ( fDec == 0.0 )
523 PushInt(0);
524 else if (fVal*fDec < 0.0)
525 PushIllegalArgument();
526 else
528 if ( !bAbs && fVal < 0.0 )
529 PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
530 else
531 PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec);
536 void ScInterpreter::ScEven()
538 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScEven" );
539 double fVal = GetDouble();
540 if (fVal < 0.0)
541 PushDouble(::rtl::math::approxFloor(fVal/2.0) * 2.0);
542 else
543 PushDouble(::rtl::math::approxCeil(fVal/2.0) * 2.0);
546 void ScInterpreter::ScOdd()
548 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScOdd" );
549 double fVal = GetDouble();
550 if (fVal >= 0.0)
552 fVal = ::rtl::math::approxCeil(fVal);
553 if (fmod(fVal, 2.0) == 0.0)
554 fVal += 1.0;
556 else
558 fVal = ::rtl::math::approxFloor(fVal);
559 if (fmod(fVal, 2.0) == 0.0)
560 fVal -= 1.0;
562 PushDouble(fVal);
565 void ScInterpreter::ScArcTan2()
567 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcTan2" );
568 if ( MustHaveParamCount( GetByte(), 2 ) )
570 double nVal2 = GetDouble();
571 double nVal1 = GetDouble();
572 PushDouble(atan2(nVal2, nVal1));
576 void ScInterpreter::ScLog()
578 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLog" );
579 BYTE nParamCount = GetByte();
580 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
582 double nBase;
583 if (nParamCount == 2)
584 nBase = GetDouble();
585 else
586 nBase = 10.0;
587 double nVal = GetDouble();
588 if (nVal > 0.0 && nBase > 0.0 && nBase != 1.0)
589 PushDouble(log(nVal) / log(nBase));
590 else
591 PushIllegalArgument();
595 void ScInterpreter::ScLn()
597 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLn" );
598 double fVal = GetDouble();
599 if (fVal > 0.0)
600 PushDouble(log(fVal));
601 else
602 PushIllegalArgument();
605 void ScInterpreter::ScLog10()
607 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLog10" );
608 double fVal = GetDouble();
609 if (fVal > 0.0)
610 PushDouble(log10(fVal));
611 else
612 PushIllegalArgument();
615 void ScInterpreter::ScNPV()
617 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScNPV" );
618 nFuncFmtType = NUMBERFORMAT_CURRENCY;
619 short nParamCount = GetByte();
620 if ( MustHaveParamCount( nParamCount, 2, 31 ) )
622 double nVal = 0.0;
623 // Wir drehen den Stack um!!
624 FormulaToken* pTemp[ 31 ];
625 for( short i = 0; i < nParamCount; i++ )
626 pTemp[ i ] = pStack[ sp - i - 1 ];
627 memcpy( &pStack[ sp - nParamCount ], pTemp, nParamCount * sizeof( FormulaToken* ) );
628 if (nGlobalError == 0)
630 double nCount = 1.0;
631 double nZins = GetDouble();
632 --nParamCount;
633 size_t nRefInList = 0;
634 ScRange aRange;
635 while (nParamCount-- > 0)
637 switch (GetStackType())
639 case svDouble :
641 nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount));
642 nCount++;
644 break;
645 case svSingleRef :
647 nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount));
648 nCount++;
650 break;
651 case svDoubleRef :
652 case svRefList :
654 USHORT nErr = 0;
655 double nCellVal;
656 PopDoubleRef( aRange, nParamCount, nRefInList);
657 ScValueIterator aValIter(pDok, aRange, glSubTotal);
658 if (aValIter.GetFirst(nCellVal, nErr))
660 nVal += (nCellVal / pow(1.0 + nZins, (double)nCount));
661 nCount++;
662 while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr))
664 nVal += (nCellVal / pow(1.0 + nZins, (double)nCount));
665 nCount++;
667 SetError(nErr);
670 break;
671 default : SetError(errIllegalParameter); break;
675 PushDouble(nVal);
679 #if defined(WIN) && defined(MSC)
680 #pragma optimize("",off)
681 #endif
683 void ScInterpreter::ScIRR()
685 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIRR" );
686 double fSchaetzwert;
687 nFuncFmtType = NUMBERFORMAT_PERCENT;
688 BYTE nParamCount = GetByte();
689 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
690 return;
691 if (nParamCount == 2)
692 fSchaetzwert = GetDouble();
693 else
694 fSchaetzwert = 0.1;
695 USHORT sPos = sp; // Stack-Position merken
696 double fEps = 1.0;
697 double x, xNeu, fWert, fZaehler, fNenner, nCount;
698 if (fSchaetzwert == -1.0)
699 x = 0.1; // default gegen Nulldivisionen
700 else
701 x = fSchaetzwert; // Startwert
702 switch (GetStackType())
704 case svDoubleRef :
705 break;
706 default:
708 PushIllegalParameter();
709 return;
712 const USHORT nIterationsMax = 20;
713 USHORT nItCount = 0;
714 ScRange aRange;
715 while (fEps > SCdEpsilon && nItCount < nIterationsMax)
716 { // Newton-Verfahren:
717 sp = sPos; // Stack zuruecksetzen
718 nCount = 0.0;
719 fZaehler = 0.0;
720 fNenner = 0.0;
721 USHORT nErr = 0;
722 PopDoubleRef( aRange );
723 ScValueIterator aValIter(pDok, aRange, glSubTotal);
724 if (aValIter.GetFirst(fWert, nErr))
726 fZaehler += fWert / pow(1.0+x,(double)nCount);
727 fNenner += -nCount * fWert / pow(1.0+x,nCount+1.0);
728 nCount++;
729 while ((nErr == 0) && aValIter.GetNext(fWert, nErr))
731 fZaehler += fWert / pow(1.0+x,(double)nCount);
732 fNenner += -nCount * fWert / pow(1.0+x,nCount+1.0);
733 nCount++;
735 SetError(nErr);
737 xNeu = x - fZaehler / fNenner; // x(i+1) = x(i)-f(x(i))/f'(x(i))
738 nItCount++;
739 fEps = fabs(xNeu - x);
740 x = xNeu;
742 if (fSchaetzwert == 0.0 && fabs(x) < SCdEpsilon)
743 x = 0.0; // auf Null normieren
744 if (fEps < SCdEpsilon)
745 PushDouble(x);
746 else
747 PushError( errNoConvergence);
749 #if defined(WIN) && defined(MSC)
750 #pragma optimize("",on)
751 #endif
754 void ScInterpreter::ScMIRR()
755 { // range_of_values ; rate_invest ; rate_reinvest
756 nFuncFmtType = NUMBERFORMAT_PERCENT;
757 if( MustHaveParamCount( GetByte(), 3 ) )
759 double fRate1_reinvest = GetDouble() + 1;
760 double fNPV_reinvest = 0.0;
761 double fPow_reinvest = 1.0;
763 double fRate1_invest = GetDouble() + 1;
764 double fNPV_invest = 0.0;
765 double fPow_invest = 1.0;
767 ScRange aRange;
768 PopDoubleRef( aRange );
770 if( nGlobalError )
771 PushError( nGlobalError);
772 else
774 ScValueIterator aValIter( pDok, aRange, glSubTotal );
775 double fCellValue;
776 ULONG nCount = 0;
777 USHORT nIterError = 0;
779 BOOL bLoop = aValIter.GetFirst( fCellValue, nIterError );
780 while( bLoop )
782 if( fCellValue > 0.0 ) // reinvestments
783 fNPV_reinvest += fCellValue * fPow_reinvest;
784 else if( fCellValue < 0.0 ) // investments
785 fNPV_invest += fCellValue * fPow_invest;
786 fPow_reinvest /= fRate1_reinvest;
787 fPow_invest /= fRate1_invest;
788 nCount++;
790 bLoop = aValIter.GetNext( fCellValue, nIterError );
792 if( nIterError )
793 PushError( nIterError );
794 else
796 double fResult = -fNPV_reinvest / fNPV_invest;
797 fResult *= pow( fRate1_reinvest, (double) nCount - 1 );
798 fResult = pow( fResult, 1.0 / (nCount - 1) );
799 PushDouble( fResult - 1.0 );
806 void ScInterpreter::ScISPMT()
807 { // rate ; period ; total_periods ; invest
808 if( MustHaveParamCount( GetByte(), 4 ) )
810 double fInvest = GetDouble();
811 double fTotal = GetDouble();
812 double fPeriod = GetDouble();
813 double fRate = GetDouble();
815 if( nGlobalError )
816 PushError( nGlobalError);
817 else
818 PushDouble( fInvest * fRate * (fPeriod / fTotal - 1.0) );
823 //----------------------- Finanzfunktionen ------------------------------------
825 double ScInterpreter::ScGetBw(double fZins, double fZzr, double fRmz,
826 double fZw, double fF)
828 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMIRR" );
829 double fBw;
830 if (fZins == 0.0)
831 fBw = fZw + fRmz * fZzr;
832 else if (fF > 0.0)
833 fBw = (fZw * pow(1.0 + fZins, -fZzr))
834 + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr + 1.0)) / fZins)
835 + fRmz;
836 else
837 fBw = (fZw * pow(1.0 + fZins, -fZzr))
838 + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr)) / fZins);
839 return -fBw;
842 void ScInterpreter::ScBW()
844 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScBW" );
845 nFuncFmtType = NUMBERFORMAT_CURRENCY;
846 double nRmz, nZzr, nZins, nZw = 0, nFlag = 0;
847 BYTE nParamCount = GetByte();
848 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
849 return;
850 if (nParamCount == 5)
851 nFlag = GetDouble();
852 if (nParamCount >= 4)
853 nZw = GetDouble();
854 nRmz = GetDouble();
855 nZzr = GetDouble();
856 nZins = GetDouble();
857 PushDouble(ScGetBw(nZins, nZzr, nRmz, nZw, nFlag));
860 void ScInterpreter::ScDIA()
862 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDIA" );
863 nFuncFmtType = NUMBERFORMAT_CURRENCY;
864 if ( MustHaveParamCount( GetByte(), 4 ) )
866 double nZr = GetDouble();
867 double nDauer = GetDouble();
868 double nRest = GetDouble();
869 double nWert = GetDouble();
870 double nDia = ((nWert - nRest) * (nDauer - nZr + 1.0)) /
871 ((nDauer * (nDauer + 1.0)) / 2.0);
872 PushDouble(nDia);
876 double ScInterpreter::ScGetGDA(double fWert, double fRest, double fDauer,
877 double fPeriode, double fFaktor)
879 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetGDA" );
880 double fGda, fZins, fAlterWert, fNeuerWert;
881 fZins = fFaktor / fDauer;
882 if (fZins >= 1.0)
884 fZins = 1.0;
885 if (fPeriode == 1.0)
886 fAlterWert = fWert;
887 else
888 fAlterWert = 0.0;
890 else
891 fAlterWert = fWert * pow(1.0 - fZins, fPeriode - 1.0);
892 fNeuerWert = fWert * pow(1.0 - fZins, fPeriode);
894 if (fNeuerWert < fRest)
895 fGda = fAlterWert - fRest;
896 else
897 fGda = fAlterWert - fNeuerWert;
898 if (fGda < 0.0)
899 fGda = 0.0;
900 return fGda;
903 void ScInterpreter::ScGDA()
905 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGDA" );
906 nFuncFmtType = NUMBERFORMAT_CURRENCY;
907 BYTE nParamCount = GetByte();
908 if ( MustHaveParamCount( nParamCount, 4, 5 ) )
910 double nFaktor;
911 if (nParamCount == 5)
912 nFaktor = GetDouble();
913 else
914 nFaktor = 2.0;
915 double nPeriode = GetDouble();
916 double nDauer = GetDouble();
917 double nRest = GetDouble();
918 double nWert = GetDouble();
919 if (nWert < 0.0 || nRest < 0.0 || nFaktor <= 0.0 || nRest > nWert
920 || nPeriode < 1.0 || nPeriode > nDauer)
921 PushIllegalArgument();
922 else
923 PushDouble(ScGetGDA(nWert, nRest, nDauer, nPeriode, nFaktor));
927 void ScInterpreter::ScGDA2()
929 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGDA2" );
930 nFuncFmtType = NUMBERFORMAT_CURRENCY;
931 BYTE nParamCount = GetByte();
932 if ( !MustHaveParamCount( nParamCount, 4, 5 ) )
933 return ;
934 double nMonate;
935 if (nParamCount == 4)
936 nMonate = 12.0;
937 else
938 nMonate = ::rtl::math::approxFloor(GetDouble());
939 double nPeriode = GetDouble();
940 double nDauer = GetDouble();
941 double nRest = GetDouble();
942 double nWert = GetDouble();
943 if (nMonate < 1.0 || nMonate > 12.0 || nDauer > 1200.0 || nRest < 0.0 ||
944 nPeriode > (nDauer + 1.0) || nRest > nWert || nWert < 0.0)
946 PushIllegalArgument();
947 return;
949 double nAbRate = 1.0 - pow(nRest / nWert, 1.0 / nDauer);
950 nAbRate = ::rtl::math::approxFloor((nAbRate * 1000.0) + 0.5) / 1000.0;
951 double nErsteAbRate = nWert * nAbRate * nMonate / 12.0;
952 double nGda2 = 0.0;
953 if (::rtl::math::approxFloor(nPeriode) == 1)
954 nGda2 = nErsteAbRate;
955 else
957 double nSummAbRate = nErsteAbRate;
958 double nMin = nDauer;
959 if (nMin > nPeriode) nMin = nPeriode;
960 USHORT iMax = (USHORT)::rtl::math::approxFloor(nMin);
961 for (USHORT i = 2; i <= iMax; i++)
963 nGda2 = (nWert - nSummAbRate) * nAbRate;
964 nSummAbRate += nGda2;
966 if (nPeriode > nDauer)
967 nGda2 = ((nWert - nSummAbRate) * nAbRate * (12.0 - nMonate)) / 12.0;
969 PushDouble(nGda2);
973 double ScInterpreter::ScInterVDB(double fWert,double fRest,double fDauer,
974 double fDauer1,double fPeriode,double fFaktor)
976 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScInterVDB" );
977 double fVdb=0;
978 double fIntEnd = ::rtl::math::approxCeil(fPeriode);
979 ULONG nLoopEnd = (ULONG) fIntEnd;
981 double fTerm, fLia;
982 double fRestwert = fWert - fRest;
983 BOOL bNowLia = FALSE;
985 double fGda;
986 ULONG i;
987 fLia=0;
988 for ( i = 1; i <= nLoopEnd; i++)
990 if(!bNowLia)
992 fGda = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor);
993 fLia = fRestwert/ (fDauer1 - (double) (i-1));
995 if (fLia > fGda)
997 fTerm = fLia;
998 bNowLia = TRUE;
1000 else
1002 fTerm = fGda;
1003 fRestwert -= fGda;
1006 else
1008 fTerm = fLia;
1011 if ( i == nLoopEnd)
1012 fTerm *= ( fPeriode + 1.0 - fIntEnd );
1014 fVdb += fTerm;
1016 return fVdb;
1020 inline double DblMin( double a, double b )
1022 return (a < b) ? a : b;
1025 void ScInterpreter::ScVDB()
1027 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScVDB" );
1028 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1029 BYTE nParamCount = GetByte();
1030 if ( MustHaveParamCount( nParamCount, 5, 7 ) )
1032 double fWert, fRest, fDauer, fAnfang, fEnde, fFaktor, fVdb = 0.0;
1033 BOOL bFlag;
1034 if (nParamCount == 7)
1035 bFlag = GetBool();
1036 else
1037 bFlag = FALSE;
1038 if (nParamCount >= 6)
1039 fFaktor = GetDouble();
1040 else
1041 fFaktor = 2.0;
1042 fEnde = GetDouble();
1043 fAnfang = GetDouble();
1044 fDauer = GetDouble();
1045 fRest = GetDouble();
1046 fWert = GetDouble();
1047 if (fAnfang < 0.0 || fEnde < fAnfang || fEnde > fDauer || fWert < 0.0
1048 || fRest > fWert || fFaktor <= 0.0)
1049 PushIllegalArgument();
1050 else
1052 double fIntStart = ::rtl::math::approxFloor(fAnfang);
1053 double fIntEnd = ::rtl::math::approxCeil(fEnde);
1054 ULONG nLoopStart = (ULONG) fIntStart;
1055 ULONG nLoopEnd = (ULONG) fIntEnd;
1057 fVdb = 0.0;
1058 if (bFlag)
1060 for (ULONG i = nLoopStart + 1; i <= nLoopEnd; i++)
1062 double fTerm = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor);
1064 // Teilperioden am Anfang / Ende beruecksichtigen:
1065 if ( i == nLoopStart+1 )
1066 fTerm *= ( DblMin( fEnde, fIntStart + 1.0 ) - fAnfang );
1067 else if ( i == nLoopEnd )
1068 fTerm *= ( fEnde + 1.0 - fIntEnd );
1070 fVdb += fTerm;
1073 else
1076 double fDauer1=fDauer;
1077 double fPart;
1079 //@Die Frage aller Fragen: "Ist das hier richtig"
1080 if(!::rtl::math::approxEqual(fAnfang,::rtl::math::approxFloor(fAnfang)))
1082 if(fFaktor>1)
1084 if(fAnfang>fDauer/2 || ::rtl::math::approxEqual(fAnfang,fDauer/2))
1086 fPart=fAnfang-fDauer/2;
1087 fAnfang=fDauer/2;
1088 fEnde-=fPart;
1089 fDauer1+=1;
1094 fWert-=ScInterVDB(fWert,fRest,fDauer,fDauer1,fAnfang,fFaktor);
1095 fVdb=ScInterVDB(fWert,fRest,fDauer,fDauer-fAnfang,fEnde-fAnfang,fFaktor);
1098 PushDouble(fVdb);
1102 void ScInterpreter::ScLaufz()
1104 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLaufz" );
1105 if ( MustHaveParamCount( GetByte(), 3 ) )
1107 double nZukunft = GetDouble();
1108 double nGegenwart = GetDouble();
1109 double nZins = GetDouble();
1110 PushDouble(log(nZukunft / nGegenwart) / log(1.0 + nZins));
1114 void ScInterpreter::ScLIA()
1116 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLIA" );
1117 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1118 if ( MustHaveParamCount( GetByte(), 3 ) )
1120 double nDauer = GetDouble();
1121 double nRest = GetDouble();
1122 double nWert = GetDouble();
1123 PushDouble((nWert - nRest) / nDauer);
1127 double ScInterpreter::ScGetRmz(double fZins, double fZzr, double fBw,
1128 double fZw, double fF)
1130 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetRmz" );
1131 double fRmz;
1132 if (fZins == 0.0)
1133 fRmz = (fBw + fZw) / fZzr;
1134 else
1136 double fTerm = pow(1.0 + fZins, fZzr);
1137 if (fF > 0.0)
1138 fRmz = (fZw * fZins / (fTerm - 1.0)
1139 + fBw * fZins / (1.0 - 1.0 / fTerm)) / (1.0+fZins);
1140 else
1141 fRmz = fZw * fZins / (fTerm - 1.0)
1142 + fBw * fZins / (1.0 - 1.0 / fTerm);
1144 return -fRmz;
1147 void ScInterpreter::ScRMZ()
1149 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRMZ" );
1150 double nZins, nZzr, nBw, nZw = 0, nFlag = 0;
1151 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1152 BYTE nParamCount = GetByte();
1153 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
1154 return;
1155 if (nParamCount == 5)
1156 nFlag = GetDouble();
1157 if (nParamCount >= 4)
1158 nZw = GetDouble();
1159 nBw = GetDouble();
1160 nZzr = GetDouble();
1161 nZins = GetDouble();
1162 PushDouble(ScGetRmz(nZins, nZzr, nBw, nZw, nFlag));
1165 void ScInterpreter::ScZGZ()
1167 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScZGZ" );
1168 nFuncFmtType = NUMBERFORMAT_PERCENT;
1169 if ( MustHaveParamCount( GetByte(), 3 ) )
1171 double nZukunftswert = GetDouble();
1172 double nGegenwartswert = GetDouble();
1173 double nZeitraum = GetDouble();
1174 PushDouble(pow(nZukunftswert / nGegenwartswert, 1.0 / nZeitraum) - 1.0);
1178 double ScInterpreter::ScGetZw(double fZins, double fZzr, double fRmz,
1179 double fBw, double fF)
1181 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetZw" );
1182 double fZw;
1183 if (fZins == 0.0)
1184 fZw = fBw + fRmz * fZzr;
1185 else
1187 double fTerm = pow(1.0 + fZins, fZzr);
1188 if (fF > 0.0)
1189 fZw = fBw * fTerm + fRmz*(1.0 + fZins)*(fTerm - 1.0)/fZins;
1190 else
1191 fZw = fBw * fTerm + fRmz*(fTerm - 1.0)/fZins;
1193 return -fZw;
1196 void ScInterpreter::ScZW()
1198 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScZW" );
1199 double nZins, nZzr, nRmz, nBw = 0, nFlag = 0;
1200 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1201 BYTE nParamCount = GetByte();
1202 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
1203 return;
1204 if (nParamCount == 5)
1205 nFlag = GetDouble();
1206 if (nParamCount >= 4)
1207 nBw = GetDouble();
1208 nRmz = GetDouble();
1209 nZzr = GetDouble();
1210 nZins = GetDouble();
1211 PushDouble(ScGetZw(nZins, nZzr, nRmz, nBw, nFlag));
1214 void ScInterpreter::ScZZR()
1216 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScZZR" );
1217 double nZins, nRmz, nBw, nZw = 0, nFlag = 0;
1218 BYTE nParamCount = GetByte();
1219 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
1220 return;
1221 if (nParamCount == 5)
1222 nFlag = GetDouble();
1223 if (nParamCount >= 4)
1224 nZw = GetDouble();
1225 nBw = GetDouble();
1226 nRmz = GetDouble();
1227 nZins = GetDouble();
1228 if (nZins == 0.0)
1229 PushDouble(-(nBw + nZw)/nRmz);
1230 else if (nFlag > 0.0)
1231 PushDouble(log(-(nZins*nZw-nRmz*(1.0+nZins))/(nZins*nBw+nRmz*(1.0+nZins)))
1232 /log(1.0+nZins));
1233 else
1234 PushDouble(log(-(nZins*nZw-nRmz)/(nZins*nBw+nRmz))/log(1.0+nZins));
1237 bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
1238 double fFv, double fPayType, double & fGuess )
1240 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::RateIteration" );
1241 // See also #i15090#
1242 // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i))
1243 // This solution handles integer and non-integer values of Nper different.
1244 // If ODFF will constraint Nper to integer, the distinction of cases can be
1245 // removed; only the integer-part is needed then.
1246 bool bValid = true, bFound = false;
1247 double fX, fXnew, fTerm, fTermDerivation;
1248 double fGeoSeries, fGeoSeriesDerivation;
1249 const USHORT nIterationsMax = 150;
1250 USHORT nCount = 0;
1251 const double fEpsilonSmall = 1.0E-14;
1252 // convert any fPayType situation to fPayType == zero situation
1253 fFv = fFv - fPayment * fPayType;
1254 fPv = fPv + fPayment * fPayType;
1255 if (fNper == ::rtl::math::round( fNper, 0, rtl_math_RoundingMode_Corrected ))
1256 { // Nper is an integer value
1257 fX = fGuess;
1258 double fPowN, fPowNminus1; // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
1259 while (!bFound && nCount < nIterationsMax)
1261 fPowNminus1 = pow( 1.0+fX, fNper-1.0);
1262 fPowN = fPowNminus1 * (1.0+fX);
1263 if (rtl::math::approxEqual( fabs(fX), 0.0))
1265 fGeoSeries = fNper;
1266 fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
1268 else
1270 fGeoSeries = (fPowN-1.0)/fX;
1271 fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;
1273 fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;
1274 fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation;
1275 if (fabs(fTerm) < fEpsilonSmall)
1276 bFound = true; // will catch root which is at an extreme
1277 else
1279 if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
1280 fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
1281 else
1282 fXnew = fX - fTerm / fTermDerivation;
1283 nCount++;
1284 // more accuracy not possible in oscillating cases
1285 bFound = (fabs(fXnew - fX) < SCdEpsilon);
1286 fX = fXnew;
1289 // Gnumeric returns roots < -1, Excel gives an error in that cases,
1290 // ODFF says nothing about it. Enable the statement, if you want Excel's
1291 // behavior
1292 //bValid =(fX >=-1.0);
1294 else
1295 { // Nper is not an integer value.
1296 fX = (fGuess < -1.0) ? -1.0 : fGuess; // start with a valid fX
1297 while (bValid && !bFound && nCount < nIterationsMax)
1299 if (rtl::math::approxEqual( fabs(fX), 0.0))
1301 fGeoSeries = fNper;
1302 fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
1304 else
1306 fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;
1307 fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;
1309 fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;
1310 fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation;
1311 if (fabs(fTerm) < fEpsilonSmall)
1312 bFound = true; // will catch root which is at an extreme
1313 else
1315 if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
1316 fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
1317 else
1318 fXnew = fX - fTerm / fTermDerivation;
1319 nCount++;
1320 // more accuracy not possible in oscillating cases
1321 bFound = (fabs(fXnew - fX) < SCdEpsilon);
1322 fX = fXnew;
1323 bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail
1327 fGuess = fX; // return approximate root
1328 return bValid && bFound;
1331 // In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess)
1332 void ScInterpreter::ScZins()
1334 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScZins" );
1335 double fPv, fPayment, fNper;
1336 // defaults for missing arguments, see ODFF spec
1337 double fFv = 0, fPayType = 0, fGuess = 0.1;
1338 bool bValid = true;
1339 nFuncFmtType = NUMBERFORMAT_PERCENT;
1340 BYTE nParamCount = GetByte();
1341 if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
1342 return;
1343 if (nParamCount == 6)
1344 fGuess = GetDouble();
1345 if (nParamCount >= 5)
1346 fPayType = GetDouble();
1347 if (nParamCount >= 4)
1348 fFv = GetDouble();
1349 fPv = GetDouble();
1350 fPayment = GetDouble();
1351 fNper = GetDouble();
1352 if (fNper <= 0.0) // constraint from ODFF spec
1354 PushIllegalArgument();
1355 return;
1357 // other values for fPayType might be meaningful,
1358 // ODFF spec is not clear yet, enable statement if you want only 0 and 1
1359 //if (fPayType != 0.0) fPayType = 1.0;
1360 bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
1361 if (!bValid)
1362 SetError(errNoConvergence);
1363 PushDouble(fGuess);
1366 double ScInterpreter::ScGetZinsZ(double fZins, double fZr, double fZzr, double fBw,
1367 double fZw, double fF, double& fRmz)
1369 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetZinsZ" );
1370 fRmz = ScGetRmz(fZins, fZzr, fBw, fZw, fF); // fuer kapz auch bei fZr == 1
1371 double fZinsZ;
1372 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1373 if (fZr == 1.0)
1375 if (fF > 0.0)
1376 fZinsZ = 0.0;
1377 else
1378 fZinsZ = -fBw;
1380 else
1382 if (fF > 0.0)
1383 fZinsZ = ScGetZw(fZins, fZr-2.0, fRmz, fBw, 1.0) - fRmz;
1384 else
1385 fZinsZ = ScGetZw(fZins, fZr-1.0, fRmz, fBw, 0.0);
1387 return fZinsZ * fZins;
1390 void ScInterpreter::ScZinsZ()
1392 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScZinsZ" );
1393 double nZins, nZr, nRmz, nZzr, nBw, nZw = 0, nFlag = 0;
1394 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1395 BYTE nParamCount = GetByte();
1396 if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
1397 return;
1398 if (nParamCount == 6)
1399 nFlag = GetDouble();
1400 if (nParamCount >= 5)
1401 nZw = GetDouble();
1402 nBw = GetDouble();
1403 nZzr = GetDouble();
1404 nZr = GetDouble();
1405 nZins = GetDouble();
1406 if (nZr < 1.0 || nZr > nZzr)
1407 PushIllegalArgument();
1408 else
1409 PushDouble(ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz));
1412 void ScInterpreter::ScKapz()
1414 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScKapz" );
1415 double nZins, nZr, nZzr, nBw, nZw = 0, nFlag = 0, nRmz, nZinsz;
1416 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1417 BYTE nParamCount = GetByte();
1418 if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
1419 return;
1420 if (nParamCount == 6)
1421 nFlag = GetDouble();
1422 if (nParamCount >= 5)
1423 nZw = GetDouble();
1424 nBw = GetDouble();
1425 nZzr = GetDouble();
1426 nZr = GetDouble();
1427 nZins = GetDouble();
1428 if (nZr < 1.0 || nZr > nZzr)
1429 PushIllegalArgument();
1430 else
1432 nZinsz = ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz);
1433 PushDouble(nRmz - nZinsz);
1437 void ScInterpreter::ScKumZinsZ()
1439 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScKumZinsZ" );
1440 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1441 if ( MustHaveParamCount( GetByte(), 6 ) )
1443 double fZins, fZzr, fBw, fAnfang, fEnde, fF, fRmz, fZinsZ;
1444 fF = GetDouble();
1445 fEnde = ::rtl::math::approxFloor(GetDouble());
1446 fAnfang = ::rtl::math::approxFloor(GetDouble());
1447 fBw = GetDouble();
1448 fZzr = GetDouble();
1449 fZins = GetDouble();
1450 if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 ||
1451 fEnde > fZzr || fZzr <= 0.0 || fBw <= 0.0)
1452 PushIllegalArgument();
1453 else
1455 ULONG nAnfang = (ULONG) fAnfang;
1456 ULONG nEnde = (ULONG) fEnde ;
1457 fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF);
1458 fZinsZ = 0.0;
1459 if (nAnfang == 1)
1461 if (fF <= 0.0)
1462 fZinsZ = -fBw;
1463 nAnfang++;
1465 for (ULONG i = nAnfang; i <= nEnde; i++)
1467 if (fF > 0.0)
1468 fZinsZ += ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz;
1469 else
1470 fZinsZ += ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0);
1472 fZinsZ *= fZins;
1473 PushDouble(fZinsZ);
1478 void ScInterpreter::ScKumKapZ()
1480 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScKumKapZ" );
1481 nFuncFmtType = NUMBERFORMAT_CURRENCY;
1482 if ( MustHaveParamCount( GetByte(), 6 ) )
1484 double fZins, fZzr, fBw, fAnfang, fEnde, fF, fRmz, fKapZ;
1485 fF = GetDouble();
1486 fEnde = ::rtl::math::approxFloor(GetDouble());
1487 fAnfang = ::rtl::math::approxFloor(GetDouble());
1488 fBw = GetDouble();
1489 fZzr = GetDouble();
1490 fZins = GetDouble();
1491 if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 ||
1492 fEnde > fZzr || fZzr <= 0.0 || fBw <= 0.0)
1493 PushIllegalArgument();
1494 else
1496 fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF);
1497 fKapZ = 0.0;
1498 ULONG nAnfang = (ULONG) fAnfang;
1499 ULONG nEnde = (ULONG) fEnde;
1500 if (nAnfang == 1)
1502 if (fF <= 0.0)
1503 fKapZ = fRmz + fBw * fZins;
1504 else
1505 fKapZ = fRmz;
1506 nAnfang++;
1508 for (ULONG i = nAnfang; i <= nEnde; i++)
1510 if (fF > 0.0)
1511 fKapZ += fRmz - (ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz) * fZins;
1512 else
1513 fKapZ += fRmz - ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0) * fZins;
1515 PushDouble(fKapZ);
1520 void ScInterpreter::ScEffektiv()
1522 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScEffektiv" );
1523 nFuncFmtType = NUMBERFORMAT_PERCENT;
1524 if ( MustHaveParamCount( GetByte(), 2 ) )
1526 double fPerioden = GetDouble();
1527 double fNominal = GetDouble();
1528 if (fPerioden < 1.0 || fNominal <= 0.0)
1529 PushIllegalArgument();
1530 else
1532 fPerioden = ::rtl::math::approxFloor(fPerioden);
1533 PushDouble(pow(1.0 + fNominal/fPerioden, fPerioden) - 1.0);
1538 void ScInterpreter::ScNominal()
1540 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScNominal" );
1541 nFuncFmtType = NUMBERFORMAT_PERCENT;
1542 if ( MustHaveParamCount( GetByte(), 2 ) )
1544 double fPerioden = GetDouble();
1545 double fEffektiv = GetDouble();
1546 if (fPerioden < 1.0 || fEffektiv <= 0.0)
1547 PushIllegalArgument();
1548 else
1550 fPerioden = ::rtl::math::approxFloor(fPerioden);
1551 PushDouble( (pow(fEffektiv + 1.0, 1.0 / fPerioden) - 1.0) * fPerioden );
1556 void ScInterpreter::ScMod()
1558 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMod" );
1559 if ( MustHaveParamCount( GetByte(), 2 ) )
1561 double nVal2 = GetDouble();
1562 double nVal1 = GetDouble();
1563 PushDouble( ::rtl::math::approxSub( nVal1,
1564 ::rtl::math::approxFloor(nVal1 / nVal2) * nVal2));
1568 /** (Goal Seek) Find a value of x that is a root of f(x)
1570 This function is used internally for the goal seek operation. It uses the
1571 Regula Falsi (aka false position) algorithm to find a root of f(x). The
1572 start value and the target value are to be given by the user in the
1573 goal seek dialog. The f(x) in this case is defined as the formula in the
1574 formula cell minus target value. This function may also perform additional
1575 search in the horizontal directions when the f(x) is discrete in order to
1576 ensure a non-zero slope necessary for deriving a subsequent x that is
1577 reasonably close to the root of interest.
1579 @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org)
1581 @see #i28955#
1583 void ScInterpreter::ScBackSolver()
1585 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScBackSolver" );
1586 if ( MustHaveParamCount( GetByte(), 3 ) )
1588 BOOL bDoneIteration = FALSE;
1589 ScAddress aValueAdr, aFormulaAdr;
1590 double fTargetVal = GetDouble();
1591 PopSingleRef( aFormulaAdr );
1592 PopSingleRef( aValueAdr );
1594 if (nGlobalError == 0)
1596 ScBaseCell* pVCell = GetCell( aValueAdr );
1597 // CELLTYPE_NOTE: kein Value aber von Formel referiert
1598 BOOL bTempCell = (!pVCell || pVCell->GetCellType() == CELLTYPE_NOTE);
1599 ScBaseCell* pFCell = GetCell( aFormulaAdr );
1601 if ( ((pVCell && pVCell->GetCellType() == CELLTYPE_VALUE) || bTempCell)
1602 && pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA )
1604 ScRange aVRange( aValueAdr, aValueAdr ); // fuer SetDirty
1605 double fSaveVal; // Original value to be restored later if necessary
1606 ScPostIt* pNote = 0;
1608 if ( bTempCell )
1610 pNote = pVCell ? pVCell->ReleaseNote() : 0;
1611 fSaveVal = 0.0;
1612 pVCell = new ScValueCell( fSaveVal );
1613 pDok->PutCell( aValueAdr, pVCell );
1615 else
1616 fSaveVal = GetCellValue( aValueAdr, pVCell );
1618 const USHORT nMaxIter = 100;
1619 const double fEps = 1E-10;
1620 const double fDelta = 1E-6;
1622 double fBestX, fXPrev;
1623 double fBestF, fFPrev;
1624 fBestX = fXPrev = fSaveVal;
1626 ScFormulaCell* pFormula = (ScFormulaCell*) pFCell;
1627 ScValueCell* pValue = (ScValueCell*) pVCell;
1629 pFormula->Interpret();
1630 BOOL bError = ( pFormula->GetErrCode() != 0 );
1631 // bError always corresponds with fF
1633 fFPrev = pFormula->GetValue() - fTargetVal;
1635 fBestF = fabs( fFPrev );
1636 if ( fBestF < fDelta )
1637 bDoneIteration = TRUE;
1639 double fX = fXPrev + fEps;
1640 double fF = fFPrev;
1641 double fSlope;
1643 USHORT nIter = 0;
1645 BOOL bHorMoveError = FALSE;
1646 // Nach der Regula Falsi Methode
1647 while ( !bDoneIteration && ( nIter++ < nMaxIter ) )
1649 pValue->SetValue( fX );
1650 pDok->SetDirty( aVRange );
1651 pFormula->Interpret();
1652 bError = ( pFormula->GetErrCode() != 0 );
1653 fF = pFormula->GetValue() - fTargetVal;
1655 if ( fF == fFPrev && !bError )
1657 // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x)
1658 // becomes different from the previous f(x). This routine is needed
1659 // when a given function is discrete, in which case the resulting slope
1660 // may become zero which ultimately causes the goal seek operation
1661 // to fail. #i28955#
1663 USHORT nHorIter = 0;
1664 const double fHorStepAngle = 5.0;
1665 const double fHorMaxAngle = 80.0;
1666 int nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle );
1667 BOOL bDoneHorMove = FALSE;
1669 while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter )
1671 double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter );
1672 double fHorTangent = ::rtl::math::tan( fHorAngle * F_PI / 180 );
1674 USHORT nIdx = 0;
1675 while( nIdx++ < 2 && !bDoneHorMove )
1677 double fHorX;
1678 if ( nIdx == 1 )
1679 fHorX = fX + fabs(fF)*fHorTangent;
1680 else
1681 fHorX = fX - fabs(fF)*fHorTangent;
1683 pValue->SetValue( fHorX );
1684 pDok->SetDirty( aVRange );
1685 pFormula->Interpret();
1686 bHorMoveError = ( pFormula->GetErrCode() != 0 );
1687 if ( bHorMoveError )
1688 break;
1690 fF = pFormula->GetValue() - fTargetVal;
1691 if ( fF != fFPrev )
1693 fX = fHorX;
1694 bDoneHorMove = TRUE;
1698 if ( !bDoneHorMove )
1699 bHorMoveError = TRUE;
1702 if ( bError )
1704 // move closer to last valid value (fXPrev), keep fXPrev & fFPrev
1705 double fDiff = ( fXPrev - fX ) / 2;
1706 if (fabs(fDiff) < fEps)
1707 fDiff = (fDiff < 0.0) ? - fEps : fEps;
1708 fX += fDiff;
1710 else if ( bHorMoveError )
1711 break;
1712 else if ( fabs(fF) < fDelta )
1714 // converged to root
1715 fBestX = fX;
1716 bDoneIteration = TRUE;
1718 else
1720 if ( fabs(fF) + fDelta < fBestF )
1722 fBestX = fX;
1723 fBestF = fabs(fF);
1726 if ( ( fXPrev - fX ) != 0 )
1728 fSlope = ( fFPrev - fF ) / ( fXPrev - fX );
1729 if ( fabs( fSlope ) < fEps )
1730 fSlope = fSlope < 0.0 ? -fEps : fEps;
1732 else
1733 fSlope = fEps;
1735 fXPrev = fX;
1736 fFPrev = fF;
1737 fX = fX - ( fF / fSlope );
1741 // Try a nice rounded input value if possible.
1742 const double fNiceDelta = (bDoneIteration && fabs(fBestX) >= 1e-3 ? 1e-3 : fDelta);
1743 double nX = ::rtl::math::approxFloor((fBestX / fNiceDelta) + 0.5) * fNiceDelta;
1744 // double nX = ::rtl::math::approxFloor((fBestX / fDelta) + 0.5) * fDelta;
1746 if ( bDoneIteration )
1748 pValue->SetValue( nX );
1749 pDok->SetDirty( aVRange );
1750 pFormula->Interpret();
1751 if ( fabs( pFormula->GetValue() - fTargetVal ) > fabs( fF ) )
1752 nX = fBestX;
1754 else if ( bError || bHorMoveError )
1756 nX = fBestX;
1758 if ( bTempCell )
1760 pVCell = pNote ? new ScNoteCell( pNote ) : 0;
1761 pDok->PutCell( aValueAdr, pVCell );
1763 else
1764 pValue->SetValue( fSaveVal );
1765 pDok->SetDirty( aVRange );
1766 pFormula->Interpret();
1767 if ( !bDoneIteration )
1768 SetError(NOTAVAILABLE);
1769 PushDouble(nX);
1771 else
1773 if ( !bDoneIteration )
1774 SetError(NOTAVAILABLE);
1775 PushInt(0); // falsche Zelltypen
1778 else
1780 if ( !bDoneIteration )
1781 SetError(NOTAVAILABLE);
1782 PushInt(0); // nGlobalError
1787 void ScInterpreter::ScIntersect()
1789 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIntersect" );
1790 formula::FormulaTokenRef p2nd = PopToken();
1791 formula::FormulaTokenRef p1st = PopToken();
1793 if (nGlobalError || !p2nd || !p1st)
1795 PushIllegalArgument();
1796 return;
1797 } // if (nGlobalError || !xT2 || !xT1)
1799 StackVar sv1 = p1st->GetType();
1800 StackVar sv2 = p2nd->GetType();
1801 if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
1802 (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
1804 PushIllegalArgument();
1805 return;
1808 ScToken* x1 = static_cast<ScToken*>(p1st.get());
1809 ScToken* x2 = static_cast<ScToken*>(p2nd.get());
1810 if (sv1 == svRefList || sv2 == svRefList)
1812 // Now this is a bit nasty but it simplifies things, and having
1813 // intersections with lists isn't too common, if at all..
1814 // Convert a reference to list.
1815 ScToken* xt[2] = { x1, x2 };
1816 StackVar sv[2] = { sv1, sv2 };
1817 for (size_t i=0; i<2; ++i)
1819 if (sv[i] == svSingleRef)
1821 ScComplexRefData aRef;
1822 aRef.Ref1 = aRef.Ref2 = xt[i]->GetSingleRef();
1823 xt[i] = new ScRefListToken;
1824 xt[i]->GetRefList()->push_back( aRef);
1826 else if (sv[i] == svDoubleRef)
1828 ScComplexRefData aRef = xt[i]->GetDoubleRef();
1829 xt[i] = new ScRefListToken;
1830 xt[i]->GetRefList()->push_back( aRef);
1833 x1 = xt[0], x2 = xt[1];
1835 x1->CalcAbsIfRel( aPos);
1836 x2->CalcAbsIfRel( aPos);
1837 ScTokenRef xRes = new ScRefListToken;
1838 ScRefList* pRefList = xRes->GetRefList();
1839 ScRefList::const_iterator end1( x1->GetRefList()->end());
1840 ScRefList::const_iterator end2( x2->GetRefList()->end());
1841 for (ScRefList::const_iterator it1( x1->GetRefList()->begin());
1842 it1 != end1; ++it1)
1844 const ScSingleRefData& r11 = (*it1).Ref1;
1845 const ScSingleRefData& r12 = (*it1).Ref2;
1846 for (ScRefList::const_iterator it2( x2->GetRefList()->begin());
1847 it2 != end2; ++it2)
1849 const ScSingleRefData& r21 = (*it2).Ref1;
1850 const ScSingleRefData& r22 = (*it2).Ref2;
1851 SCCOL nCol1 = ::std::max( r11.nCol, r21.nCol);
1852 SCROW nRow1 = ::std::max( r11.nRow, r21.nRow);
1853 SCTAB nTab1 = ::std::max( r11.nTab, r21.nTab);
1854 SCCOL nCol2 = ::std::min( r12.nCol, r22.nCol);
1855 SCROW nRow2 = ::std::min( r12.nRow, r22.nRow);
1856 SCTAB nTab2 = ::std::min( r12.nTab, r22.nTab);
1857 if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
1858 ; // nothing
1859 else
1861 ScComplexRefData aRef;
1862 aRef.InitRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1863 pRefList->push_back( aRef);
1867 size_t n = pRefList->size();
1868 if (!n)
1869 PushError( errNoRef);
1870 else if (n == 1)
1872 const ScComplexRefData& rRef = (*pRefList)[0];
1873 if (rRef.Ref1 == rRef.Ref2)
1874 PushTempToken( new ScSingleRefToken( rRef.Ref1));
1875 else
1876 PushTempToken( new ScDoubleRefToken( rRef));
1878 else
1879 PushTempToken( xRes);
1881 else
1883 ScToken* pt[2] = { x1, x2 };
1884 StackVar sv[2] = { sv1, sv2 };
1885 SCCOL nC1[2], nC2[2];
1886 SCROW nR1[2], nR2[2];
1887 SCTAB nT1[2], nT2[2];
1888 for (size_t i=0; i<2; ++i)
1890 switch (sv[i])
1892 case svSingleRef:
1893 case svDoubleRef:
1894 pt[i]->CalcAbsIfRel( aPos);
1896 const ScSingleRefData& r = pt[i]->GetSingleRef();
1897 nC1[i] = r.nCol;
1898 nR1[i] = r.nRow;
1899 nT1[i] = r.nTab;
1901 if (sv[i] == svDoubleRef)
1903 const ScSingleRefData& r = pt[i]->GetSingleRef2();
1904 nC2[i] = r.nCol;
1905 nR2[i] = r.nRow;
1906 nT2[i] = r.nTab;
1908 else
1910 nC2[i] = nC1[i];
1911 nR2[i] = nR1[i];
1912 nT2[i] = nT1[i];
1914 break;
1915 default:
1916 ; // nothing, prevent compiler warning
1919 SCCOL nCol1 = ::std::max( nC1[0], nC1[1]);
1920 SCROW nRow1 = ::std::max( nR1[0], nR1[1]);
1921 SCTAB nTab1 = ::std::max( nT1[0], nT1[1]);
1922 SCCOL nCol2 = ::std::min( nC2[0], nC2[1]);
1923 SCROW nRow2 = ::std::min( nR2[0], nR2[1]);
1924 SCTAB nTab2 = ::std::min( nT2[0], nT2[1]);
1925 if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
1926 PushError( errNoRef);
1927 else if (nCol2 == nCol1 && nRow2 == nRow1 && nTab2 == nTab1)
1928 PushSingleRef( nCol1, nRow1, nTab1);
1929 else
1930 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1935 void ScInterpreter::ScRangeFunc()
1937 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRangeFunc" );
1938 formula::FormulaTokenRef x2 = PopToken();
1939 formula::FormulaTokenRef x1 = PopToken();
1941 if (nGlobalError || !x2 || !x1)
1943 PushIllegalArgument();
1944 return;
1945 } // if (nGlobalError || !xT2 || !xT1)
1946 FormulaTokenRef xRes = ScToken::ExtendRangeReference( *x1, *x2, aPos, false);
1947 if (!xRes)
1948 PushIllegalArgument();
1949 else
1950 PushTempToken( xRes);
1954 void ScInterpreter::ScUnionFunc()
1956 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScUnionFunc" );
1957 formula::FormulaTokenRef p2nd = PopToken();
1958 formula::FormulaTokenRef p1st = PopToken();
1960 if (nGlobalError || !p2nd || !p1st)
1962 PushIllegalArgument();
1963 return;
1964 } // if (nGlobalError || !xT2 || !xT1)
1966 StackVar sv1 = p1st->GetType();
1967 StackVar sv2 = p2nd->GetType();
1968 if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
1969 (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
1971 PushIllegalArgument();
1972 return;
1975 ScToken* x1 = static_cast<ScToken*>(p1st.get());
1976 ScToken* x2 = static_cast<ScToken*>(p2nd.get());
1979 ScTokenRef xRes;
1980 // Append to an existing RefList if there is one.
1981 if (sv1 == svRefList)
1983 xRes = x1;
1984 sv1 = svUnknown; // mark as handled
1986 else if (sv2 == svRefList)
1988 xRes = x2;
1989 sv2 = svUnknown; // mark as handled
1991 else
1992 xRes = new ScRefListToken;
1993 ScRefList* pRes = xRes->GetRefList();
1994 ScToken* pt[2] = { x1, x2 };
1995 StackVar sv[2] = { sv1, sv2 };
1996 for (size_t i=0; i<2; ++i)
1998 if (pt[i] == xRes)
1999 continue;
2000 switch (sv[i])
2002 case svSingleRef:
2004 ScComplexRefData aRef;
2005 aRef.Ref1 = aRef.Ref2 = pt[i]->GetSingleRef();
2006 pRes->push_back( aRef);
2008 break;
2009 case svDoubleRef:
2010 pRes->push_back( pt[i]->GetDoubleRef());
2011 break;
2012 case svRefList:
2014 const ScRefList* p = pt[i]->GetRefList();
2015 ScRefList::const_iterator it( p->begin());
2016 ScRefList::const_iterator end( p->end());
2017 for ( ; it != end; ++it)
2019 pRes->push_back( *it);
2022 break;
2023 default:
2024 ; // nothing, prevent compiler warning
2027 ValidateRef( *pRes); // set #REF! if needed
2028 PushTempToken( xRes);
2032 void ScInterpreter::ScCurrent()
2034 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCurrent" );
2035 FormulaTokenRef xTok( PopToken());
2036 if (xTok)
2038 PushTempToken( xTok);
2039 PushTempToken( xTok);
2041 else
2042 PushError( errUnknownStackVariable);
2045 void ScInterpreter::ScStyle()
2047 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScStyle" );
2048 BYTE nParamCount = GetByte();
2049 if (nParamCount >= 1 && nParamCount <= 3)
2051 String aStyle2; // Vorlage nach Timer
2052 if (nParamCount >= 3)
2053 aStyle2 = GetString();
2054 long nTimeOut = 0; // Timeout
2055 if (nParamCount >= 2)
2056 nTimeOut = (long)(GetDouble()*1000.0);
2057 String aStyle1 = GetString(); // Vorlage fuer sofort
2059 if (nTimeOut < 0)
2060 nTimeOut = 0;
2063 // Request ausfuehren, um Vorlage anzuwenden
2066 if ( !pDok->IsClipOrUndo() )
2068 SfxObjectShell* pShell = pDok->GetDocumentShell();
2069 if (pShell)
2071 //! notify object shell directly
2073 ScRange aRange(aPos);
2074 ScAutoStyleHint aHint( aRange, aStyle1, nTimeOut, aStyle2 );
2075 pShell->Broadcast( aHint );
2079 PushDouble(0.0);
2081 else
2082 PushIllegalParameter();
2085 ScDdeLink* lcl_GetDdeLink( SvxLinkManager* pLinkMgr,
2086 const String& rA, const String& rT, const String& rI, BYTE nM )
2088 USHORT nCount = pLinkMgr->GetLinks().Count();
2089 for (USHORT i=0; i<nCount; i++ )
2091 ::sfx2::SvBaseLink* pBase = *pLinkMgr->GetLinks()[i];
2092 if (pBase->ISA(ScDdeLink))
2094 ScDdeLink* pLink = (ScDdeLink*)pBase;
2095 if ( pLink->GetAppl() == rA &&
2096 pLink->GetTopic() == rT &&
2097 pLink->GetItem() == rI &&
2098 pLink->GetMode() == nM )
2099 return pLink;
2103 return NULL;
2106 void ScInterpreter::ScDde()
2108 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDde" );
2109 // Applikation, Datei, Bereich
2110 // Application, Topic, Item
2112 BYTE nParamCount = GetByte();
2113 if ( MustHaveParamCount( nParamCount, 3, 4 ) )
2115 BYTE nMode = SC_DDE_DEFAULT;
2116 if (nParamCount == 4)
2117 nMode = (BYTE) ::rtl::math::approxFloor(GetDouble());
2118 String aItem = GetString();
2119 String aTopic = GetString();
2120 String aAppl = GetString();
2122 if (nMode > SC_DDE_TEXT)
2123 nMode = SC_DDE_DEFAULT;
2125 // temporary documents (ScFunctionAccess) have no DocShell
2126 // and no LinkManager -> abort
2128 SvxLinkManager* pLinkMgr = pDok->GetLinkManager();
2129 if (!pLinkMgr)
2131 PushNoValue();
2132 return;
2135 // Nach dem Laden muss neu interpretiert werden (Verknuepfungen aufbauen)
2137 if ( pMyFormulaCell->GetCode()->IsRecalcModeNormal() )
2138 pMyFormulaCell->GetCode()->SetRecalcModeOnLoad();
2140 // solange der Link nicht ausgewertet ist, Idle abklemmen
2141 // (um zirkulaere Referenzen zu vermeiden)
2143 BOOL bOldDis = pDok->IsIdleDisabled();
2144 pDok->DisableIdle( TRUE );
2146 // Link-Objekt holen / anlegen
2148 ScDdeLink* pLink = lcl_GetDdeLink( pLinkMgr, aAppl, aTopic, aItem, nMode );
2150 //! Dde-Links (zusaetzlich) effizienter am Dokument speichern !!!!!
2151 // ScDdeLink* pLink = pDok->GetDdeLink( aAppl, aTopic, aItem );
2153 BOOL bWasError = ( pMyFormulaCell->GetRawError() != 0 );
2155 if (!pLink)
2157 pLink = new ScDdeLink( pDok, aAppl, aTopic, aItem, nMode );
2158 pLinkMgr->InsertDDELink( pLink, aAppl, aTopic, aItem );
2159 if ( pLinkMgr->GetLinks().Count() == 1 ) // erster ?
2161 SfxBindings* pBindings = pDok->GetViewBindings();
2162 if (pBindings)
2163 pBindings->Invalidate( SID_LINKS ); // Link-Manager enablen
2166 //! asynchron auswerten ???
2167 pLink->TryUpdate(); // TryUpdate ruft Update nicht mehrfach auf
2169 // StartListening erst nach dem Update, sonst circular reference
2170 pMyFormulaCell->StartListening( *pLink );
2172 else
2174 pMyFormulaCell->StartListening( *pLink );
2177 // Wenn aus dem Reschedule beim Ausfuehren des Links ein Fehler
2178 // (z.B. zirkulaere Referenz) entstanden ist, der vorher nicht da war,
2179 // das Fehler-Flag zuruecksetzen:
2181 if ( pMyFormulaCell->GetRawError() && !bWasError )
2182 pMyFormulaCell->SetErrCode(0);
2184 // Wert abfragen
2186 const ScMatrix* pLinkMat = pLink->GetResult();
2187 if (pLinkMat)
2189 SCSIZE nC, nR;
2190 pLinkMat->GetDimensions(nC, nR);
2191 ScMatrixRef pNewMat = GetNewMat( nC, nR);
2192 if (pNewMat)
2194 pLinkMat->MatCopy(*pNewMat); // kopieren
2195 PushMatrix( pNewMat );
2197 else
2198 PushIllegalArgument();
2200 else
2201 PushNA();
2203 pDok->DisableIdle( bOldDis );
2207 void ScInterpreter::ScBase()
2208 { // Value, Base [, MinLen]
2209 BYTE nParamCount = GetByte();
2210 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
2212 static const sal_Unicode __FAR_DATA pDigits[] = {
2213 '0','1','2','3','4','5','6','7','8','9',
2214 'A','B','C','D','E','F','G','H','I','J','K','L','M',
2215 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
2218 static const int nDigits = (sizeof(pDigits)/sizeof(sal_Unicode))-1;
2219 xub_StrLen nMinLen;
2220 if ( nParamCount == 3 )
2222 double fLen = ::rtl::math::approxFloor( GetDouble() );
2223 if ( 1.0 <= fLen && fLen < STRING_MAXLEN )
2224 nMinLen = (xub_StrLen) fLen;
2225 else if ( fLen == 0.0 )
2226 nMinLen = 1;
2227 else
2228 nMinLen = 0; // Error
2230 else
2231 nMinLen = 1;
2232 double fBase = ::rtl::math::approxFloor( GetDouble() );
2233 double fVal = ::rtl::math::approxFloor( GetDouble() );
2234 double fChars = ((fVal > 0.0 && fBase > 0.0) ?
2235 (ceil( log( fVal ) / log( fBase ) ) + 2.0) :
2236 2.0);
2237 if ( fChars >= STRING_MAXLEN )
2238 nMinLen = 0; // Error
2240 if ( !nGlobalError && nMinLen && 2 <= fBase && fBase <= nDigits && 0 <= fVal )
2242 const xub_StrLen nConstBuf = 128;
2243 sal_Unicode aBuf[nConstBuf];
2244 xub_StrLen nBuf = Max( (xub_StrLen) fChars, (xub_StrLen) (nMinLen+1) );
2245 sal_Unicode* pBuf = (nBuf <= nConstBuf ? aBuf : new sal_Unicode[nBuf]);
2246 for ( xub_StrLen j = 0; j < nBuf; ++j )
2248 pBuf[j] = '0';
2250 sal_Unicode* p = pBuf + nBuf - 1;
2251 *p = 0;
2252 if ( fVal <= (ULONG)(~0) )
2254 ULONG nVal = (ULONG) fVal;
2255 ULONG nBase = (ULONG) fBase;
2256 while ( nVal && p > pBuf )
2258 *--p = pDigits[ nVal % nBase ];
2259 nVal /= nBase;
2261 fVal = (double) nVal;
2263 else
2265 BOOL bDirt = FALSE;
2266 while ( fVal && p > pBuf )
2268 //! mit fmod Rundungsfehler ab 2**48
2269 // double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) );
2270 // so ist es etwas besser
2271 double fInt = ::rtl::math::approxFloor( fVal / fBase );
2272 double fMult = fInt * fBase;
2273 #if OSL_DEBUG_LEVEL > 1
2274 // #53943# =BASIS(1e308;36) => GPF mit
2275 // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult );
2276 // trotz vorheriger Pruefung ob fVal >= fMult
2277 double fDebug1 = fVal - fMult;
2278 // fVal := 7,5975311883090e+290
2279 // fMult := 7,5975311883090e+290
2280 // fDebug1 := 1,3848924157003e+275 <- RoundOff-Error
2281 // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE
2282 double fDebug2 = ::rtl::math::approxSub( fVal, fMult );
2283 // und ::rtl::math::approxSub( fVal, fMult ) == 0
2284 double fDebug3 = ( fInt ? fVal / fInt : 0.0 );
2285 // Nach dem strange fDebug1 und fVal < fMult ist eigentlich
2286 // fDebug2 == fBase, trotzdem wird das mit einem Vergleich
2287 // nicht erkannt, dann schlaegt bDirt zu und alles wird wieder gut..
2289 // prevent compiler warnings
2290 (void)fDebug1; (void)fDebug2; (void)fDebug3;
2291 #endif
2292 size_t nDig;
2293 if ( fVal < fMult )
2294 { // da ist was gekippt
2295 bDirt = TRUE;
2296 nDig = 0;
2298 else
2300 double fDig = ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal, fMult ) );
2301 if ( bDirt )
2303 bDirt = FALSE;
2304 --fDig;
2306 if ( fDig <= 0.0 )
2307 nDig = 0;
2308 else if ( fDig >= fBase )
2309 nDig = ((size_t) fBase) - 1;
2310 else
2311 nDig = (size_t) fDig;
2313 *--p = pDigits[ nDig ];
2314 fVal = fInt;
2317 if ( fVal )
2318 PushError( errStringOverflow );
2319 else
2321 if ( nBuf - (p - pBuf) <= nMinLen )
2322 p = pBuf + nBuf - 1 - nMinLen;
2323 PushStringBuffer( p );
2325 if ( pBuf != aBuf )
2326 delete [] pBuf;
2328 else
2329 PushIllegalArgument();
2334 void ScInterpreter::ScDecimal()
2335 { // Text, Base
2336 if ( MustHaveParamCount( GetByte(), 2 ) )
2338 double fBase = ::rtl::math::approxFloor( GetDouble() );
2339 String aStr( GetString() );
2340 if ( !nGlobalError && 2 <= fBase && fBase <= 36 )
2342 double fVal = 0.0;
2343 int nBase = (int) fBase;
2344 register const sal_Unicode* p = aStr.GetBuffer();
2345 while ( *p == ' ' || *p == '\t' )
2346 p++; // strip leading white space
2347 if ( nBase == 16 )
2348 { // evtl. hex-prefix strippen
2349 if ( *p == 'x' || *p == 'X' )
2350 p++;
2351 else if ( *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X') )
2352 p += 2;
2354 while ( *p )
2356 int n;
2357 if ( '0' <= *p && *p <= '9' )
2358 n = *p - '0';
2359 else if ( 'A' <= *p && *p <= 'Z' )
2360 n = 10 + (*p - 'A');
2361 else if ( 'a' <= *p && *p <= 'z' )
2362 n = 10 + (*p - 'a');
2363 else
2364 n = nBase;
2365 if ( nBase <= n )
2367 if ( *(p+1) == 0 &&
2368 ( (nBase == 2 && (*p == 'b' || *p == 'B'))
2369 ||(nBase == 16 && (*p == 'h' || *p == 'H')) )
2371 ; // 101b und F00Dh sind ok
2372 else
2374 PushIllegalArgument();
2375 return ;
2378 else
2379 fVal = fVal * fBase + n;
2380 p++;
2383 PushDouble( fVal );
2385 else
2386 PushIllegalArgument();
2391 void ScInterpreter::ScConvert()
2392 { // Value, FromUnit, ToUnit
2393 if ( MustHaveParamCount( GetByte(), 3 ) )
2395 String aToUnit( GetString() );
2396 String aFromUnit( GetString() );
2397 double fVal = GetDouble();
2398 if ( nGlobalError )
2399 PushError( nGlobalError);
2400 else
2401 { // erst die angegebene Reihenfolge suchen, wenn nicht gefunden den Kehrwert
2402 double fConv;
2403 if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) )
2404 PushDouble( fVal * fConv );
2405 else if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) )
2406 PushDouble( fVal / fConv );
2407 else
2408 PushNA();
2414 void ScInterpreter::ScRoman()
2415 { // Value [Mode]
2416 BYTE nParamCount = GetByte();
2417 if( MustHaveParamCount( nParamCount, 1, 2 ) )
2419 double fMode = (nParamCount == 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0;
2420 double fVal = ::rtl::math::approxFloor( GetDouble() );
2421 if( nGlobalError )
2422 PushError( nGlobalError);
2423 else if( (fMode >= 0.0) && (fMode < 5.0) && (fVal >= 0.0) && (fVal < 4000.0) )
2425 static const sal_Unicode pChars[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
2426 static const USHORT pValues[] = { 1000, 500, 100, 50, 10, 5, 1 };
2427 static const USHORT nMaxIndex = (USHORT)(sizeof(pValues) / sizeof(pValues[0]) - 1);
2429 String aRoman;
2430 USHORT nVal = (USHORT) fVal;
2431 USHORT nMode = (USHORT) fMode;
2433 for( UINT16 i = 0; i <= nMaxIndex / 2; i++ )
2435 USHORT nIndex = 2 * i;
2436 USHORT nDigit = nVal / pValues[ nIndex ];
2438 if( (nDigit % 5) == 4 )
2440 USHORT nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2;
2441 USHORT nSteps = 0;
2442 while( (nSteps < nMode) && (nIndex < nMaxIndex) )
2444 nSteps++;
2445 if( pValues[ nIndex2 ] - pValues[ nIndex + 1 ] <= nVal )
2446 nIndex++;
2447 else
2448 nSteps = nMode;
2450 aRoman += pChars[ nIndex ];
2451 aRoman += pChars[ nIndex2 ];
2452 nVal = sal::static_int_cast<USHORT>( nVal + pValues[ nIndex ] );
2453 nVal = sal::static_int_cast<USHORT>( nVal - pValues[ nIndex2 ] );
2455 else
2457 if( nDigit > 4 )
2458 aRoman += pChars[ nIndex - 1 ];
2459 aRoman.Expand( aRoman.Len() + (nDigit % 5), pChars[ nIndex ] );
2460 nVal %= pValues[ nIndex ];
2464 PushString( aRoman );
2466 else
2467 PushIllegalArgument();
2472 BOOL lcl_GetArabicValue( sal_Unicode cChar, USHORT& rnValue, BOOL& rbIsDec )
2474 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScBase" );
2475 switch( cChar )
2477 case 'M': rnValue = 1000; rbIsDec = TRUE; break;
2478 case 'D': rnValue = 500; rbIsDec = FALSE; break;
2479 case 'C': rnValue = 100; rbIsDec = TRUE; break;
2480 case 'L': rnValue = 50; rbIsDec = FALSE; break;
2481 case 'X': rnValue = 10; rbIsDec = TRUE; break;
2482 case 'V': rnValue = 5; rbIsDec = FALSE; break;
2483 case 'I': rnValue = 1; rbIsDec = TRUE; break;
2484 default: return FALSE;
2486 return TRUE;
2490 void ScInterpreter::ScArabic()
2492 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArabic" );
2493 String aRoman( GetString() );
2494 if( nGlobalError )
2495 PushError( nGlobalError);
2496 else
2498 aRoman.ToUpperAscii();
2500 USHORT nValue = 0;
2501 USHORT nValidRest = 3999;
2502 USHORT nCharIndex = 0;
2503 USHORT nCharCount = aRoman.Len();
2504 BOOL bValid = TRUE;
2506 while( bValid && (nCharIndex < nCharCount) )
2508 USHORT nDigit1 = 0;
2509 USHORT nDigit2 = 0;
2510 BOOL bIsDec1 = FALSE;
2511 BOOL bIsDec2 = FALSE;
2512 bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex ), nDigit1, bIsDec1 );
2513 if( bValid && (nCharIndex + 1 < nCharCount) )
2514 bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex + 1 ), nDigit2, bIsDec2 );
2515 if( bValid )
2517 if( nDigit1 >= nDigit2 )
2519 nValue = sal::static_int_cast<USHORT>( nValue + nDigit1 );
2520 nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2));
2521 bValid = (nValidRest >= nDigit1);
2522 if( bValid )
2523 nValidRest = sal::static_int_cast<USHORT>( nValidRest - nDigit1 );
2524 nCharIndex++;
2526 else if( nDigit1 * 2 != nDigit2 )
2528 USHORT nDiff = nDigit2 - nDigit1;
2529 nValue = sal::static_int_cast<USHORT>( nValue + nDiff );
2530 bValid = (nValidRest >= nDiff);
2531 if( bValid )
2532 nValidRest = nDigit1 - 1;
2533 nCharIndex += 2;
2535 else
2536 bValid = FALSE;
2539 if( bValid )
2540 PushInt( nValue );
2541 else
2542 PushIllegalArgument();
2547 void ScInterpreter::ScHyperLink()
2549 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScHyperLink" );
2550 BYTE nParamCount = GetByte();
2551 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
2553 String aCellText = GetString();
2554 ScMatrixRef pResMat = GetNewMat(1,2);
2555 pResMat->PutString(aCellText,0);
2556 pResMat->PutString((nParamCount == 2) ? GetString() : aCellText, 1);
2557 bMatrixFormula = true;
2558 PushMatrix(pResMat);
2561 BOOL lclConvertMoney( const String& aSearchUnit, double& rfRate, int& rnDec )
2563 struct ConvertInfo
2565 const sal_Char* pCurrText;
2566 double fRate;
2567 int nDec;
2569 ConvertInfo aConvertTable[] = {
2570 { "EUR", 1.0, 2 },
2571 { "ATS", 13.7603, 2 },
2572 { "BEF", 40.3399, 0 },
2573 { "DEM", 1.95583, 2 },
2574 { "ESP", 166.386, 0 },
2575 { "FIM", 5.94573, 2 },
2576 { "FRF", 6.55957, 2 },
2577 { "IEP", 0.787564, 2 },
2578 { "ITL", 1936.27, 0 },
2579 { "LUF", 40.3399, 0 },
2580 { "NLG", 2.20371, 2 },
2581 { "PTE", 200.482, 2 },
2582 { "GRD", 340.750, 2 },
2583 { "SIT", 239.640, 2 },
2584 { "MTL", 0.429300, 2 },
2585 { "CYP", 0.585274, 2 },
2586 { "SKK", 30.1260, 2 }
2589 const size_t nConversionCount = sizeof( aConvertTable ) / sizeof( aConvertTable[0] );
2590 for ( size_t i = 0; i < nConversionCount; i++ )
2591 if ( aSearchUnit.EqualsIgnoreCaseAscii( aConvertTable[i].pCurrText ) )
2593 rfRate = aConvertTable[i].fRate;
2594 rnDec = aConvertTable[i].nDec;
2595 return TRUE;
2597 return FALSE;
2600 void ScInterpreter::ScEuroConvert()
2601 { //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]]
2602 BYTE nParamCount = GetByte();
2603 if ( MustHaveParamCount( nParamCount, 3, 5 ) )
2605 double nPrecision = 0.0;
2606 if ( nParamCount == 5 )
2608 nPrecision = ::rtl::math::approxFloor(GetDouble());
2609 if ( nPrecision < 3 )
2611 PushIllegalArgument();
2612 return;
2615 BOOL bFullPrecision = FALSE;
2616 if ( nParamCount >= 4 )
2617 bFullPrecision = GetBool();
2618 String aToUnit( GetString() );
2619 String aFromUnit( GetString() );
2620 double fVal = GetDouble();
2621 if ( nGlobalError )
2622 PushError( nGlobalError);
2623 else
2625 double fRes;
2626 double fFromRate;
2627 double fToRate;
2628 int nFromDec;
2629 int nToDec;
2630 String aEur( RTL_CONSTASCII_USTRINGPARAM("EUR"));
2631 if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec )
2632 && lclConvertMoney( aToUnit, fToRate, nToDec ) )
2634 if ( aFromUnit.EqualsIgnoreCaseAscii( aToUnit ) )
2635 fRes = fVal;
2636 else
2638 if ( aFromUnit.EqualsIgnoreCaseAscii( aEur ) )
2639 fRes = fVal * fToRate;
2640 else
2642 double fIntermediate = fVal / fFromRate;
2643 if ( nPrecision )
2644 fIntermediate = ::rtl::math::round( fIntermediate,
2645 (int) nPrecision );
2646 fRes = fIntermediate * fToRate;
2648 if ( !bFullPrecision )
2649 fRes = ::rtl::math::round( fRes, nToDec );
2651 PushDouble( fRes );
2653 else
2654 PushIllegalArgument();
2660 // BAHTTEXT ===================================================================
2662 #define UTF8_TH_0 "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
2663 #define UTF8_TH_1 "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
2664 #define UTF8_TH_2 "\340\270\252\340\270\255\340\270\207"
2665 #define UTF8_TH_3 "\340\270\252\340\270\262\340\270\241"
2666 #define UTF8_TH_4 "\340\270\252\340\270\265\340\271\210"
2667 #define UTF8_TH_5 "\340\270\253\340\271\211\340\270\262"
2668 #define UTF8_TH_6 "\340\270\253\340\270\201"
2669 #define UTF8_TH_7 "\340\271\200\340\270\210\340\271\207\340\270\224"
2670 #define UTF8_TH_8 "\340\271\201\340\270\233\340\270\224"
2671 #define UTF8_TH_9 "\340\271\200\340\270\201\340\271\211\340\270\262"
2672 #define UTF8_TH_10 "\340\270\252\340\270\264\340\270\232"
2673 #define UTF8_TH_11 "\340\271\200\340\270\255\340\271\207\340\270\224"
2674 #define UTF8_TH_20 "\340\270\242\340\270\265\340\271\210"
2675 #define UTF8_TH_1E2 "\340\270\243\340\271\211\340\270\255\340\270\242"
2676 #define UTF8_TH_1E3 "\340\270\236\340\270\261\340\270\231"
2677 #define UTF8_TH_1E4 "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
2678 #define UTF8_TH_1E5 "\340\271\201\340\270\252\340\270\231"
2679 #define UTF8_TH_1E6 "\340\270\245\340\271\211\340\270\262\340\270\231"
2680 #define UTF8_TH_DOT0 "\340\270\226\340\271\211\340\270\247\340\270\231"
2681 #define UTF8_TH_BAHT "\340\270\232\340\270\262\340\270\227"
2682 #define UTF8_TH_SATANG "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
2683 #define UTF8_TH_MINUS "\340\270\245\340\270\232"
2685 #define UTF8_STRINGPARAM( ascii ) ascii, static_cast< xub_StrLen >( sizeof( ascii ) - 1 )
2686 #define UTF8_CREATE( ascii ) ByteString( UTF8_STRINGPARAM( ascii ) )
2687 #define UTF8_APPEND( ascii ) Append( UTF8_STRINGPARAM( ascii ) )
2688 #define UTF8_PREPEND( ascii ) Insert( UTF8_CREATE( ascii ), 0 )
2690 // local functions ------------------------------------------------------------
2692 namespace {
2694 inline void lclSplitBlock( double& rfInt, sal_Int32& rnBlock, double fValue, double fSize )
2696 rnBlock = static_cast< sal_Int32 >( modf( (fValue + 0.1) / fSize, &rfInt ) * fSize + 0.1 );
2699 /** Appends a digit (0 to 9) to the passed string. */
2700 void lclAppendDigit( ByteString& rText, sal_Int32 nDigit )
2702 switch( nDigit )
2704 case 0: rText.UTF8_APPEND( UTF8_TH_0 ); break;
2705 case 1: rText.UTF8_APPEND( UTF8_TH_1 ); break;
2706 case 2: rText.UTF8_APPEND( UTF8_TH_2 ); break;
2707 case 3: rText.UTF8_APPEND( UTF8_TH_3 ); break;
2708 case 4: rText.UTF8_APPEND( UTF8_TH_4 ); break;
2709 case 5: rText.UTF8_APPEND( UTF8_TH_5 ); break;
2710 case 6: rText.UTF8_APPEND( UTF8_TH_6 ); break;
2711 case 7: rText.UTF8_APPEND( UTF8_TH_7 ); break;
2712 case 8: rText.UTF8_APPEND( UTF8_TH_8 ); break;
2713 case 9: rText.UTF8_APPEND( UTF8_TH_9 ); break;
2714 default: DBG_ERRORFILE( "lclAppendDigit - illegal digit" );
2718 /** Appends a value raised to a power of 10: nDigit*10^nPow10.
2719 @param nDigit A digit in the range from 1 to 9.
2720 @param nPow10 A value in the range from 2 to 5.
2722 void lclAppendPow10( ByteString& rText, sal_Int32 nDigit, sal_Int32 nPow10 )
2724 DBG_ASSERT( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" );
2725 lclAppendDigit( rText, nDigit );
2726 switch( nPow10 )
2728 case 2: rText.UTF8_APPEND( UTF8_TH_1E2 ); break;
2729 case 3: rText.UTF8_APPEND( UTF8_TH_1E3 ); break;
2730 case 4: rText.UTF8_APPEND( UTF8_TH_1E4 ); break;
2731 case 5: rText.UTF8_APPEND( UTF8_TH_1E5 ); break;
2732 default: DBG_ERRORFILE( "lclAppendPow10 - illegal power" );
2736 /** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
2737 void lclAppendBlock( ByteString& rText, sal_Int32 nValue )
2739 DBG_ASSERT( (1 <= nValue) && (nValue <= 999999), "lclAppendBlock - illegal value" );
2740 if( nValue >= 100000 )
2742 lclAppendPow10( rText, nValue / 100000, 5 );
2743 nValue %= 100000;
2745 if( nValue >= 10000 )
2747 lclAppendPow10( rText, nValue / 10000, 4 );
2748 nValue %= 10000;
2750 if( nValue >= 1000 )
2752 lclAppendPow10( rText, nValue / 1000, 3 );
2753 nValue %= 1000;
2755 if( nValue >= 100 )
2757 lclAppendPow10( rText, nValue / 100, 2 );
2758 nValue %= 100;
2760 if( nValue > 0 )
2762 sal_Int32 nTen = nValue / 10;
2763 sal_Int32 nOne = nValue % 10;
2764 if( nTen >= 1 )
2766 if( nTen >= 3 )
2767 lclAppendDigit( rText, nTen );
2768 else if( nTen == 2 )
2769 rText.UTF8_APPEND( UTF8_TH_20 );
2770 rText.UTF8_APPEND( UTF8_TH_10 );
2772 if( (nTen > 0) && (nOne == 1) )
2773 rText.UTF8_APPEND( UTF8_TH_11 );
2774 else if( nOne > 0 )
2775 lclAppendDigit( rText, nOne );
2779 } // namespace
2781 // ----------------------------------------------------------------------------
2783 void ScInterpreter::ScBahtText()
2785 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScBahtText" );
2786 BYTE nParamCount = GetByte();
2787 if ( MustHaveParamCount( nParamCount, 1 ) )
2789 double fValue = GetDouble();
2790 if( nGlobalError )
2792 PushError( nGlobalError);
2793 return;
2796 // sign
2797 bool bMinus = fValue < 0.0;
2798 fValue = fabs( fValue );
2800 // round to 2 digits after decimal point, fValue contains Satang as integer
2801 fValue = ::rtl::math::approxFloor( fValue * 100.0 + 0.5 );
2803 // split Baht and Satang
2804 double fBaht = 0.0;
2805 sal_Int32 nSatang = 0;
2806 lclSplitBlock( fBaht, nSatang, fValue, 100.0 );
2808 ByteString aText;
2810 // generate text for Baht value
2811 if( fBaht == 0.0 )
2813 if( nSatang == 0 )
2814 aText.UTF8_APPEND( UTF8_TH_0 );
2816 else while( fBaht > 0.0 )
2818 ByteString aBlock;
2819 sal_Int32 nBlock = 0;
2820 lclSplitBlock( fBaht, nBlock, fBaht, 1.0e6 );
2821 if( nBlock > 0 )
2822 lclAppendBlock( aBlock, nBlock );
2823 // add leading "million", if there will come more blocks
2824 if( fBaht > 0.0 )
2825 aBlock.UTF8_PREPEND( UTF8_TH_1E6 );
2826 aText.Insert( aBlock, 0 );
2828 if( aText.Len() > 0 )
2829 aText.UTF8_APPEND( UTF8_TH_BAHT );
2831 // generate text for Satang value
2832 if( nSatang == 0 )
2834 aText.UTF8_APPEND( UTF8_TH_DOT0 );
2836 else
2838 lclAppendBlock( aText, nSatang );
2839 aText.UTF8_APPEND( UTF8_TH_SATANG );
2842 // add the minus sign
2843 if( bMinus )
2844 aText.UTF8_PREPEND( UTF8_TH_MINUS );
2846 PushString( String( aText, RTL_TEXTENCODING_UTF8 ) );
2850 // ============================================================================
2852 void ScInterpreter::ScPhoneticString()
2854 BYTE nParamCount = GetByte();
2855 if ( MustHaveParamCountMin( nParamCount, 1 ) )
2857 String aPhoneticText;
2858 while( nParamCount-- )
2860 if ( nGlobalError )
2862 PushError(nGlobalError);
2863 return;
2866 switch ( GetStackType() )
2868 case svSingleRef :
2870 ScAddress aAdr;
2871 PopSingleRef( aAdr );
2872 if (nGlobalError)
2874 PushError(nGlobalError);
2875 return;
2878 ScBaseCell* pCell = GetCell( aAdr );
2879 if ( HasCellStringData( pCell ) )
2881 String aStr;
2882 GetCellPhoneticString( aStr, pCell );
2883 aPhoneticText += aStr;
2886 break;
2887 case svDoubleRef:
2889 ScRange aRange;
2890 PopDoubleRef( aRange );
2891 if (nGlobalError)
2893 PushError(nGlobalError);
2894 return;
2897 ScCellIterator aIter( pDok, aRange );
2898 ScBaseCell *pCell = aIter.GetFirst();
2899 while ( pCell )
2901 if ( HasCellStringData( pCell ) )
2903 String aStr;
2904 GetCellPhoneticString( aStr, pCell );
2905 aPhoneticText += aStr;
2907 pCell = aIter.GetNext();
2910 break;
2911 default:
2912 PushError(errIllegalParameter);
2913 return;
2916 PushString( aPhoneticText );
2920 // ============================================================================
2922 void ScInterpreter::ScGetPivotData()
2924 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGetPivotData" );
2925 BYTE nParamCount = GetByte();
2927 if ( MustHaveParamCount( nParamCount, 2, 30 ) )
2929 // there must be an even number of args
2930 // target, ref, then field/item pairs
2931 if( (nParamCount % 2) == 1)
2932 goto failed;
2934 bool bOldSyntax = false;
2935 if ( nParamCount == 2 )
2937 // if the first parameter is a ref, assume old syntax
2938 StackVar eFirstType = GetStackType( 2 );
2939 if ( eFirstType == svSingleRef || eFirstType == svDoubleRef )
2940 bOldSyntax = true;
2943 ScDPGetPivotDataField aTarget; // target field, and returns result
2944 std::vector< ScDPGetPivotDataField > aFilters;
2945 String aFilterList;
2946 if ( bOldSyntax )
2947 aFilterList = GetString(); // old syntax: second parameter is list of constraints
2948 else
2950 // new syntax: separate name/value pairs
2952 USHORT nFilterCount = nParamCount / 2 - 1;
2953 aFilters.resize( nFilterCount );
2955 USHORT i = nFilterCount;
2956 while( i-- > 0 )
2958 //! should allow numeric constraint values
2959 aFilters[i].mbValIsStr = TRUE;
2960 aFilters[i].maValStr = GetString();
2962 aFilters[i].maFieldName = GetString();
2966 // common to both syntaxes: a reference to the data pilot table
2968 ScRange aBlock;
2969 switch ( GetStackType() )
2971 case svDoubleRef :
2972 PopDoubleRef( aBlock );
2973 break;
2975 case svSingleRef :
2977 ScAddress aAddr;
2978 PopSingleRef( aAddr );
2979 aBlock = aAddr;
2980 break;
2982 default:
2983 goto failed;
2985 // NOTE : MS Excel docs claim to use the 'most recent' which is not
2986 // exactly the same as what we do in ScDocument::GetDPAtBlock
2987 // However we do need to use GetDPABlock
2988 ScDPObject* pDPObj = pDok->GetDPAtBlock ( aBlock );
2989 if( NULL == pDPObj)
2990 goto failed;
2992 if ( bOldSyntax )
2994 // fill aFilters / aTarget from aFilterList string
2995 if ( !pDPObj->ParseFilters( aTarget, aFilters, aFilterList ) )
2996 goto failed;
2998 else
2999 aTarget.maFieldName = GetString(); // new syntax: first parameter is data field name
3001 if( pDPObj->GetPivotData( aTarget, aFilters ) )
3003 if( aTarget.mbValIsStr )
3004 PushString( aTarget.maValStr );
3005 else
3006 PushDouble( aTarget.mnValNum );
3007 return;
3011 failed :
3012 PushError( errNoRef );