LCOV - code coverage report
Current view: top level - sc/source/core/tool - interpr2.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 1067 1804 59.1 %
Date: 2015-06-13 12:38:46 Functions: 71 96 74.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "interpre.hxx"
      21             : 
      22             : #include <comphelper/string.hxx>
      23             : #include <sfx2/linkmgr.hxx>
      24             : #include <sfx2/dispatch.hxx>
      25             : #include <sfx2/objsh.hxx>
      26             : #include <svl/stritem.hxx>
      27             : #include <svl/zforlist.hxx>
      28             : #include <svl/sharedstringpool.hxx>
      29             : #include <sal/macros.h>
      30             : #include <boost/math/special_functions/log1p.hpp>
      31             : 
      32             : #include "attrib.hxx"
      33             : #include "sc.hrc"
      34             : #include "ddelink.hxx"
      35             : #include "scmatrix.hxx"
      36             : #include "compiler.hxx"
      37             : #include "formulacell.hxx"
      38             : #include "document.hxx"
      39             : #include "dociter.hxx"
      40             : #include "docoptio.hxx"
      41             : #include "unitconv.hxx"
      42             : #include "globstr.hrc"
      43             : #include "hints.hxx"
      44             : #include "dpobject.hxx"
      45             : #include "postit.hxx"
      46             : #include "tokenarray.hxx"
      47             : #include "globalnames.hxx"
      48             : 
      49             : #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
      50             : 
      51             : #include <string.h>
      52             : #include <math.h>
      53             : 
      54             : using ::std::vector;
      55             : using namespace com::sun::star;
      56             : using namespace formula;
      57             : 
      58             : #define SCdEpsilon                1.0E-7
      59             : 
      60             : // Date and Time
      61             : 
      62          99 : double ScInterpreter::GetDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
      63             :         bool bStrict, bool bCheckGregorian )
      64             : {
      65          99 :     if ( nYear < 100 && !bStrict )
      66           0 :         nYear = pFormatter->ExpandTwoDigitYear( nYear );
      67             :     // Do not use a default Date ctor here because it asks system time with a
      68             :     // performance penalty.
      69             :     sal_Int16 nY, nM, nD;
      70          99 :     if (bStrict)
      71           0 :         nY = nYear, nM = nMonth, nD = nDay;
      72             :     else
      73             :     {
      74          99 :         if (nMonth > 0)
      75             :         {
      76          99 :             nY = nYear + (nMonth-1) / 12;
      77          99 :             nM = ((nMonth-1) % 12) + 1;
      78             :         }
      79             :         else
      80             :         {
      81           0 :             nY = nYear + (nMonth-12) / 12;
      82           0 :             nM = 12 - (-nMonth) % 12;
      83             :         }
      84          99 :         nD = 1;
      85             :     }
      86          99 :     Date aDate( nD, nM, nY);
      87          99 :     if (!bStrict)
      88          99 :         aDate += nDay - 1;
      89          99 :     if ((!bCheckGregorian && aDate.IsValidDate()) || (bCheckGregorian && aDate.IsValidAndGregorian()))
      90          99 :         return (double) (aDate - *(pFormatter->GetNullDate()));
      91             :     else
      92             :     {
      93           0 :         SetError(errNoValue);
      94           0 :         return 0;
      95             :     }
      96             : }
      97             : 
      98          16 : void ScInterpreter::ScGetActDate()
      99             : {
     100          16 :     nFuncFmtType = css::util::NumberFormat::DATE;
     101          16 :     Date aActDate( Date::SYSTEM );
     102          16 :     long nDiff = aActDate - *(pFormatter->GetNullDate());
     103          16 :     PushDouble((double) nDiff);
     104          16 : }
     105             : 
     106           3 : void ScInterpreter::ScGetActTime()
     107             : {
     108           3 :     nFuncFmtType = css::util::NumberFormat::DATETIME;
     109           3 :     Date aActDate( Date::SYSTEM );
     110           3 :     long nDiff = aActDate - *(pFormatter->GetNullDate());
     111           3 :     tools::Time aActTime( tools::Time::SYSTEM );
     112           6 :     double nTime = aActTime.GetHour()    / static_cast<double>(::tools::Time::hourPerDay)   +
     113           6 :                    aActTime.GetMin()     / static_cast<double>(::tools::Time::minutePerDay) +
     114           3 :                    aActTime.GetSec()     / static_cast<double>(::tools::Time::secondPerDay) +
     115           3 :                    aActTime.GetNanoSec() / static_cast<double>(::tools::Time::nanoSecPerDay);
     116           3 :     PushDouble( (double) nDiff + nTime );
     117           3 : }
     118             : 
     119           0 : void ScInterpreter::ScGetYear()
     120             : {
     121           0 :     Date aDate = *(pFormatter->GetNullDate());
     122           0 :     aDate += (long) ::rtl::math::approxFloor(GetDouble());
     123           0 :     PushDouble( (double) aDate.GetYear() );
     124           0 : }
     125             : 
     126           0 : void ScInterpreter::ScGetMonth()
     127             : {
     128           0 :     Date aDate = *(pFormatter->GetNullDate());
     129           0 :     aDate += (long) ::rtl::math::approxFloor(GetDouble());
     130           0 :     PushDouble( (double) aDate.GetMonth() );
     131           0 : }
     132             : 
     133           0 : void ScInterpreter::ScGetDay()
     134             : {
     135           0 :     Date aDate = *(pFormatter->GetNullDate());
     136           0 :     aDate += (long)::rtl::math::approxFloor(GetDouble());
     137           0 :     PushDouble((double) aDate.GetDay());
     138           0 : }
     139             : 
     140           0 : void ScInterpreter::ScGetMin()
     141             : {
     142           0 :     double fTime = GetDouble();
     143           0 :     fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
     144           0 :     long nVal = (long)::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR+0.5) % ::tools::Time::secondPerHour;
     145           0 :     PushDouble( (double) (nVal / ::tools::Time::secondPerMinute) );
     146           0 : }
     147             : 
     148           0 : void ScInterpreter::ScGetSec()
     149             : {
     150           0 :     double fTime = GetDouble();
     151           0 :     fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
     152           0 :     long nVal = (long)::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR+0.5) % ::tools::Time::secondPerMinute;
     153           0 :     PushDouble( (double) nVal );
     154           0 : }
     155             : 
     156           0 : void ScInterpreter::ScGetHour()
     157             : {
     158           0 :     double fTime = GetDouble();
     159           0 :     fTime -= ::rtl::math::approxFloor(fTime);       // Datumsanteil weg
     160           0 :     long nVal = (long)::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR+0.5) / ::tools::Time::secondPerHour;
     161           0 :     PushDouble((double) nVal);
     162           0 : }
     163             : 
     164           0 : void ScInterpreter::ScGetDateValue()
     165             : {
     166           0 :     OUString aInputString = GetString().getString();
     167           0 :     sal_uInt32 nFIndex = 0;                 // damit default Land/Spr.
     168             :     double fVal;
     169           0 :     if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
     170             :     {
     171           0 :         short eType = pFormatter->GetType(nFIndex);
     172           0 :         if (eType == css::util::NumberFormat::DATE || eType == css::util::NumberFormat::DATETIME)
     173           0 :             PushDouble(::rtl::math::approxFloor(fVal));
     174             :         else
     175           0 :             PushIllegalArgument();
     176             :     }
     177             :     else
     178           0 :         PushIllegalArgument();
     179           0 : }
     180             : 
     181           0 : void ScInterpreter::ScGetDayOfWeek()
     182             : {
     183           0 :     sal_uInt8 nParamCount = GetByte();
     184           0 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
     185             :     {
     186             :         short nFlag;
     187           0 :         if (nParamCount == 2)
     188           0 :             nFlag = (short) ::rtl::math::approxFloor(GetDouble());
     189             :         else
     190           0 :             nFlag = 1;
     191             : 
     192           0 :         Date aDate = *(pFormatter->GetNullDate());
     193           0 :         aDate += (long)::rtl::math::approxFloor(GetDouble());
     194           0 :         int nVal = (int) aDate.GetDayOfWeek();
     195           0 :         if (nFlag == 1)
     196             :         {
     197           0 :             if (nVal == 6)
     198           0 :                 nVal = 1;
     199             :             else
     200           0 :                 nVal += 2;
     201             :         }
     202           0 :         else if (nFlag == 2)
     203           0 :             nVal += 1;
     204           0 :         PushInt( nVal );
     205             :     }
     206           0 : }
     207             : 
     208           0 : void ScInterpreter::ScGetWeekOfYear()
     209             : {
     210           0 :     if ( MustHaveParamCount( GetByte(), 2 ) )
     211             :     {
     212           0 :         short nFlag = (short) ::rtl::math::approxFloor(GetDouble());
     213             : 
     214           0 :         Date aDate = *(pFormatter->GetNullDate());
     215           0 :         aDate += (long)::rtl::math::approxFloor(GetDouble());
     216           0 :         PushInt( (int) aDate.GetWeekOfYear( nFlag == 1 ? SUNDAY : MONDAY ));
     217             :     }
     218           0 : }
     219             : 
     220           0 : void ScInterpreter::ScEasterSunday()
     221             : {
     222           0 :     nFuncFmtType = css::util::NumberFormat::DATE;
     223           0 :     if ( MustHaveParamCount( GetByte(), 1 ) )
     224             :     {
     225             :         sal_Int16 nDay, nMonth, nYear;
     226           0 :         nYear = (sal_Int16) ::rtl::math::approxFloor( GetDouble() );
     227           0 :         if ( nYear < 100 )
     228           0 :             nYear = pFormatter->ExpandTwoDigitYear( nYear );
     229             :         // don't worry, be happy :)
     230             :         int B,C,D,E,F,G,H,I,K,L,M,N,O;
     231           0 :         N = nYear % 19;
     232           0 :         B = int(nYear / 100);
     233           0 :         C = nYear % 100;
     234           0 :         D = int(B / 4);
     235           0 :         E = B % 4;
     236           0 :         F = int((B + 8) / 25);
     237           0 :         G = int((B - F + 1) / 3);
     238           0 :         H = (19 * N + B - D - G + 15) % 30;
     239           0 :         I = int(C / 4);
     240           0 :         K = C % 4;
     241           0 :         L = (32 + 2 * E + 2 * I - H - K) % 7;
     242           0 :         M = int((N + 11 * H + 22 * L) / 451);
     243           0 :         O = H + L - 7 * M + 114;
     244           0 :         nDay = sal::static_int_cast<sal_Int16>( O % 31 + 1 );
     245           0 :         nMonth = sal::static_int_cast<sal_Int16>( int(O / 31) );
     246           0 :         PushDouble( GetDateSerial( nYear, nMonth, nDay, true, true ) );
     247             :     }
     248           0 : }
     249             : 
     250           0 : sal_uInt16 ScInterpreter::GetWeekendAndHolidayMasks(
     251             :     const sal_uInt8 nParamCount, const sal_uInt32 nNullDate, vector< double >& rSortArray,
     252             :     bool bWeekendMask[ 7 ] )
     253             : {
     254           0 :     if ( nParamCount == 4 )
     255             :     {
     256           0 :         vector< double > nWeekendDays;
     257           0 :         GetNumberSequenceArray( 1, nWeekendDays, false );
     258           0 :         if ( nGlobalError )
     259           0 :             return nGlobalError;
     260             :         else
     261             :         {
     262           0 :             if ( nWeekendDays.size() != 7 )
     263           0 :                 return  errIllegalArgument;
     264             : 
     265           0 :             for ( int i = 0; i < 7; i++ )
     266           0 :                 bWeekendMask[ i ] = ( bool ) nWeekendDays[ i ];
     267           0 :         }
     268             :     }
     269             :     else
     270             :     {
     271           0 :         for ( int i = 0; i < 7; i++ )
     272           0 :             bWeekendMask[ i] = false;
     273             : 
     274           0 :         bWeekendMask[ SATURDAY ] = true;
     275           0 :         bWeekendMask[ SUNDAY ]   = true;
     276             :     }
     277             : 
     278           0 :     if ( nParamCount >= 3 )
     279             :     {
     280           0 :         GetSortArray( 1, rSortArray, NULL, false );
     281           0 :         size_t nMax = rSortArray.size();
     282           0 :         for ( size_t i = 0; i < nMax; i++ )
     283           0 :             rSortArray.at( i ) = ::rtl::math::approxFloor( rSortArray.at( i ) ) + nNullDate;
     284             :     }
     285             : 
     286           0 :     return nGlobalError;
     287             : }
     288             : 
     289          27 : sal_uInt16 ScInterpreter::GetWeekendAndHolidayMasks_MS(
     290             :     const sal_uInt8 nParamCount, const sal_uInt32 nNullDate, vector< double >& rSortArray,
     291             :     bool bWeekendMask[ 7 ] )
     292             : {
     293          27 :     sal_uInt16 nErr = 0;
     294          27 :     OUString aWeekendDays;
     295          27 :     if ( nParamCount == 4 )
     296             :     {
     297          18 :         GetSortArray( 1, rSortArray, NULL, true );
     298          18 :         size_t nMax = rSortArray.size();
     299          72 :         for ( size_t i = 0; i < nMax; i++ )
     300          54 :             rSortArray.at( i ) = ::rtl::math::approxFloor( rSortArray.at( i ) ) + nNullDate;
     301             :     }
     302             : 
     303          27 :     if ( nParamCount >= 3 )
     304          21 :         aWeekendDays = GetString().getString();
     305             : 
     306         216 :     for ( int i = 0; i < 7; i++ )
     307         189 :         bWeekendMask[ i] = false;
     308             : 
     309          27 :     if ( aWeekendDays.isEmpty() )
     310             :     {
     311           9 :         bWeekendMask[ SATURDAY ] = true;
     312           9 :         bWeekendMask[ SUNDAY ]   = true;
     313             :     }
     314             :     else
     315             :     {
     316          18 :         switch ( aWeekendDays.getLength() )
     317             :         {
     318             :             case 1 :
     319             :                 // Weekend days defined by code
     320           9 :                 switch ( aWeekendDays[ 0 ] )
     321             :                 {
     322           6 :                     case '1' : bWeekendMask[ SATURDAY ]  = true; bWeekendMask[ SUNDAY ]    = true; break;
     323           3 :                     case '2' : bWeekendMask[ SUNDAY ]    = true; bWeekendMask[ MONDAY ]    = true; break;
     324           0 :                     case '3' : bWeekendMask[ MONDAY ]    = true; bWeekendMask[ TUESDAY ]   = true; break;
     325           0 :                     case '4' : bWeekendMask[ TUESDAY ]   = true; bWeekendMask[ WEDNESDAY ] = true; break;
     326           0 :                     case '5' : bWeekendMask[ WEDNESDAY ] = true; bWeekendMask[ THURSDAY ]  = true; break;
     327           0 :                     case '6' : bWeekendMask[ THURSDAY ]  = true; bWeekendMask[ FRIDAY ]    = true; break;
     328           0 :                     case '7' : bWeekendMask[ FRIDAY ]    = true; bWeekendMask[ SATURDAY ]  = true; break;
     329           0 :                     default  : nErr = errIllegalArgument;                                          break;
     330             :                 }
     331           9 :                 break;
     332             :             case 2 :
     333             :                 // Weekend day defined by code
     334           3 :                 if ( aWeekendDays[ 0 ] == '1' )
     335             :                 {
     336           3 :                     switch ( aWeekendDays[ 1 ] )
     337             :                     {
     338           0 :                         case '1' : bWeekendMask[ SUNDAY ]    = true; break;
     339           0 :                         case '2' : bWeekendMask[ MONDAY ]    = true; break;
     340           3 :                         case '3' : bWeekendMask[ TUESDAY ]   = true; break;
     341           0 :                         case '4' : bWeekendMask[ WEDNESDAY ] = true; break;
     342           0 :                         case '5' : bWeekendMask[ THURSDAY ]  = true; break;
     343           0 :                         case '6' : bWeekendMask[ FRIDAY ]    = true; break;
     344           0 :                         case '7' : bWeekendMask[ SATURDAY ]  = true; break;
     345           0 :                         default  : nErr = errIllegalArgument;        break;
     346             :                     }
     347             :                 }
     348             :                 else
     349           0 :                     nErr = errIllegalArgument;
     350           3 :                 break;
     351             :             case 7 :
     352             :                 // Weekend days defined by string
     353          48 :                 for ( int i = 0; i < 7 && !nErr; i++ )
     354             :                 {
     355          42 :                     switch ( aWeekendDays[ i ] )
     356             :                     {
     357          27 :                         case '0' : bWeekendMask[ i ] = false; break;
     358          15 :                         case '1' : bWeekendMask[ i ] = true;  break;
     359           0 :                         default  : nErr = errIllegalArgument; break;
     360             :                     }
     361             :                 }
     362           6 :                 break;
     363             :             default :
     364           0 :                 nErr = errIllegalArgument;
     365           0 :                 break;
     366             :         }
     367             :     }
     368          27 :     return nErr;
     369             : }
     370             : 
     371          12 : void ScInterpreter::ScNetWorkdays( bool bOOXML_Version )
     372             : {
     373          12 :     sal_uInt8 nParamCount = GetByte();
     374          12 :     if ( MustHaveParamCount( nParamCount, 2, 4 ) )
     375             :     {
     376          12 :         vector<double> nSortArray;
     377             :         bool bWeekendMask[ 7 ];
     378          12 :         Date aNullDate = *( pFormatter->GetNullDate() );
     379          12 :         sal_uInt32 nNullDate = Date::DateToDays( aNullDate.GetDay(), aNullDate.GetMonth(), aNullDate.GetYear() );
     380             :         sal_uInt16 nErr;
     381          12 :         if ( bOOXML_Version )
     382             :         {
     383             :             nErr = GetWeekendAndHolidayMasks_MS( nParamCount, nNullDate,
     384          12 :                             nSortArray, bWeekendMask );
     385             :         }
     386             :         else
     387             :         {
     388             :             nErr = GetWeekendAndHolidayMasks( nParamCount, nNullDate,
     389           0 :                             nSortArray, bWeekendMask );
     390             :         }
     391          12 :         if ( nErr )
     392           0 :             PushError( nErr );
     393             :         else
     394             :         {
     395          12 :             sal_uInt32 nDate2 = ( sal_uInt32 )::rtl::math::approxFloor( GetDouble() ) + nNullDate;
     396          12 :             sal_uInt32 nDate1 = ( sal_uInt32 )::rtl::math::approxFloor( GetDouble() ) + nNullDate;
     397             : 
     398          12 :             sal_Int32 nCnt = 0;
     399          12 :             size_t nRef = 0;
     400          12 :             bool bReverse = ( nDate1 > nDate2 );
     401          12 :             if ( bReverse )
     402             :             {
     403           0 :                 sal_uInt32 nTemp = nDate1;
     404           0 :                 nDate1 = nDate2;
     405           0 :                 nDate2 = nTemp;
     406             :             }
     407          12 :             size_t nMax = nSortArray.size();
     408        1161 :             while ( nDate1 <= nDate2 )
     409             :             {
     410        1137 :                 if ( !bWeekendMask[ GetDayOfWeek( nDate1 ) ] )
     411             :                 {
     412        1652 :                     while ( nRef < nMax && nSortArray.at( nRef ) < nDate1 )
     413          12 :                         nRef++;
     414         820 :                     if ( !( nRef < nMax && nSortArray.at( nRef ) == nDate1 ) )
     415         814 :                         nCnt++;
     416             :                 }
     417        1137 :                 ++nDate1;
     418             :             }
     419          12 :             PushDouble( ( double ) ( bReverse ? -nCnt : nCnt ) );
     420          12 :         }
     421             :     }
     422          12 : }
     423             : 
     424          15 : void ScInterpreter::ScWorkday_MS()
     425             : {
     426          15 :     sal_uInt8 nParamCount = GetByte();
     427          15 :     if ( MustHaveParamCount( nParamCount, 2, 4 ) )
     428             :     {
     429          15 :         nFuncFmtType = css::util::NumberFormat::DATE;
     430          15 :         vector<double> nSortArray;
     431             :         bool bWeekendMask[ 7 ];
     432          15 :         Date aNullDate = *( pFormatter->GetNullDate() );
     433          15 :         sal_uInt32 nNullDate = Date::DateToDays( aNullDate.GetDay(), aNullDate.GetMonth(), aNullDate.GetYear() );
     434             :         sal_uInt16 nErr = GetWeekendAndHolidayMasks_MS( nParamCount, nNullDate,
     435          15 :                             nSortArray, bWeekendMask );
     436          15 :         if ( nErr )
     437           0 :             PushError( nErr );
     438             :         else
     439             :         {
     440          15 :             sal_Int32 nDays = ::rtl::math::approxFloor( GetDouble() );
     441          15 :             sal_uInt32 nDate = ( sal_uInt32 )::rtl::math::approxFloor( GetDouble() ) + nNullDate;
     442             : 
     443          15 :             if ( !nDays )
     444           0 :                 PushDouble( ( double ) ( nDate - nNullDate ) );
     445             :             else
     446             :             {
     447          15 :                 size_t nMax = nSortArray.size();
     448          15 :                 if ( nDays > 0 )
     449             :                 {
     450          15 :                     size_t nRef = 0;
     451      119592 :                     while ( nDays )
     452             :                     {
     453      239163 :                         while ( nRef < nMax && nSortArray.at( nRef ) < nDate )
     454          39 :                             nRef++;
     455      119562 :                         if ( !( nRef < nMax && nSortArray.at( nRef ) == nDate ) || nRef >= nMax )
     456      119541 :                              nDays--;
     457             : 
     458      167393 :                         do
     459      167393 :                             ++nDate;
     460      167393 :                         while ( bWeekendMask[ GetDayOfWeek( nDate ) ] ); //jump over weekend day(s)
     461             :                     }
     462             :                 }
     463             :                 else
     464             :                 {
     465           0 :                     sal_Int16 nRef = nMax - 1;
     466           0 :                     while ( nDays )
     467             :                     {
     468           0 :                         while ( nRef >= 0 && nSortArray.at( nRef ) > nDate )
     469           0 :                             nRef--;
     470           0 :                         if ( !( nRef >= 0 && nSortArray.at( nRef ) == nDate ) || nRef < 0 )
     471           0 :                              nDays++;
     472             : 
     473           0 :                         do
     474           0 :                           --nDate;
     475           0 :                         while ( bWeekendMask[ GetDayOfWeek( nDate ) ] ); //jump over weekend day(s)
     476             :                     }
     477             :                 }
     478          15 :                 PushDouble( ( double ) ( nDate - nNullDate ) );
     479             :             }
     480          15 :         }
     481             :     }
     482          15 : }
     483             : 
     484          99 : void ScInterpreter::ScGetDate()
     485             : {
     486          99 :     nFuncFmtType = css::util::NumberFormat::DATE;
     487          99 :     if ( MustHaveParamCount( GetByte(), 3 ) )
     488             :     {
     489          99 :         sal_Int16 nDay   = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
     490          99 :         sal_Int16 nMonth = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
     491          99 :         sal_Int16 nYear  = (sal_Int16) ::rtl::math::approxFloor(GetDouble());
     492          99 :         if (nYear < 0)
     493           0 :             PushIllegalArgument();
     494             :         else
     495             :         {
     496          99 :             PushDouble(GetDateSerial(nYear, nMonth, nDay, false, true));
     497             :         }
     498             :     }
     499          99 : }
     500             : 
     501           0 : void ScInterpreter::ScGetTime()
     502             : {
     503           0 :     nFuncFmtType = css::util::NumberFormat::TIME;
     504           0 :     if ( MustHaveParamCount( GetByte(), 3 ) )
     505             :     {
     506           0 :         double nSec = GetDouble();
     507           0 :         double nMin = GetDouble();
     508           0 :         double nHour = GetDouble();
     509           0 :         double fTime = fmod( (nHour * ::tools::Time::secondPerHour) + (nMin * ::tools::Time::secondPerMinute) + nSec, DATE_TIME_FACTOR) / DATE_TIME_FACTOR;
     510           0 :         if (fTime < 0)
     511           0 :             PushIllegalArgument();
     512             :         else
     513           0 :             PushDouble( fTime);
     514             :     }
     515           0 : }
     516             : 
     517           0 : void ScInterpreter::ScGetDiffDate()
     518             : {
     519           0 :     if ( MustHaveParamCount( GetByte(), 2 ) )
     520             :     {
     521           0 :         double nDate2 = GetDouble();
     522           0 :         double nDate1 = GetDouble();
     523           0 :         PushDouble(nDate1 - nDate2);
     524             :     }
     525           0 : }
     526             : 
     527           0 : void ScInterpreter::ScGetDiffDate360()
     528             : {
     529             :     /* Implementation follows
     530             :      * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf
     531             :      * Appendix B: Day-Count Bases, there are 7 different ways to calculate the
     532             :      * 30-days count. That document also claims that Excel implements the "PSA
     533             :      * 30" or "NASD 30" method (funny enough they also state that Excel is the
     534             :      * only tool that does so).
     535             :      *
     536             :      * Note that the definition given in
     537             :      * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp
     538             :      * is _not_ the way how it is actually calculated by Excel (that would not
     539             :      * even match any of the 7 methods mentioned above) and would result in the
     540             :      * following test cases producing wrong results according to that appendix B:
     541             :      *
     542             :      * 28-Feb-95  31-Aug-95  181 instead of 180
     543             :      * 29-Feb-96  31-Aug-96  181 instead of 180
     544             :      * 30-Jan-96  31-Mar-96   61 instead of  60
     545             :      * 31-Jan-96  31-Mar-96   61 instead of  60
     546             :      *
     547             :      * Still, there is a difference between OOoCalc and Excel:
     548             :      * In Excel:
     549             :      * 02-Feb-99 31-Mar-00 results in  419
     550             :      * 31-Mar-00 02-Feb-99 results in -418
     551             :      * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
     552             :      */
     553             : 
     554           0 :     sal_uInt8 nParamCount = GetByte();
     555           0 :     if ( MustHaveParamCount( nParamCount, 2, 3 ) )
     556             :     {
     557             :         bool bFlag;
     558           0 :         if (nParamCount == 3)
     559           0 :             bFlag = GetBool();
     560             :         else
     561           0 :             bFlag = false;
     562           0 :         double nDate2 = GetDouble();
     563           0 :         double nDate1 = GetDouble();
     564           0 :         if (nGlobalError)
     565           0 :             PushError( nGlobalError);
     566             :         else
     567             :         {
     568             :             double fSign;
     569             :             // #i84934# only for non-US European algorithm swap dates. Else
     570             :             // follow Excel's meaningless extrapolation for "interoperability".
     571           0 :             if (bFlag && (nDate2 < nDate1))
     572             :             {
     573           0 :                 fSign = nDate1;
     574           0 :                 nDate1 = nDate2;
     575           0 :                 nDate2 = fSign;
     576           0 :                 fSign = -1.0;
     577             :             }
     578             :             else
     579           0 :                 fSign = 1.0;
     580           0 :             Date aDate1 = *(pFormatter->GetNullDate());
     581           0 :             aDate1 += (long) ::rtl::math::approxFloor(nDate1);
     582           0 :             Date aDate2 = *(pFormatter->GetNullDate());
     583           0 :             aDate2 += (long) ::rtl::math::approxFloor(nDate2);
     584           0 :             if (aDate1.GetDay() == 31)
     585           0 :                 aDate1 -= (sal_uLong) 1;
     586           0 :             else if (!bFlag)
     587             :             {
     588           0 :                 if (aDate1.GetMonth() == 2)
     589             :                 {
     590           0 :                     switch ( aDate1.GetDay() )
     591             :                     {
     592             :                         case 28 :
     593           0 :                             if ( !aDate1.IsLeapYear() )
     594           0 :                                 aDate1.SetDay(30);
     595           0 :                         break;
     596             :                         case 29 :
     597           0 :                             aDate1.SetDay(30);
     598           0 :                         break;
     599             :                     }
     600             :                 }
     601             :             }
     602           0 :             if (aDate2.GetDay() == 31)
     603             :             {
     604           0 :                 if (!bFlag )
     605             :                 {
     606           0 :                     if (aDate1.GetDay() == 30)
     607           0 :                         aDate2 -= (sal_uLong) 1;
     608             :                 }
     609             :                 else
     610           0 :                     aDate2.SetDay(30);
     611             :             }
     612             :             PushDouble( fSign * (double)
     613           0 :                 (  (double) aDate2.GetDay() + (double) aDate2.GetMonth() * 30.0 +
     614           0 :                    (double) aDate2.GetYear() * 360.0
     615           0 :                  - (double) aDate1.GetDay() - (double) aDate1.GetMonth() * 30.0
     616           0 :                  - (double)aDate1.GetYear() * 360.0) );
     617             :         }
     618             :     }
     619           0 : }
     620             : 
     621             : // fdo#44456 function DATEDIF as defined in ODF1.2 (Par. 6.10.3)
     622          30 : void ScInterpreter::ScGetDateDif()
     623             : {
     624          30 :     if ( MustHaveParamCount( GetByte(), 3 ) )
     625             :     {
     626          30 :         OUString aInterval = GetString().getString();
     627          30 :         double nDate2    = GetDouble();
     628          30 :         double nDate1    = GetDouble();
     629             : 
     630          30 :         if (nGlobalError)
     631             :         {
     632           0 :             PushError( nGlobalError);
     633           0 :             return;
     634             :         }
     635             : 
     636             :         // Excel doesn't swap dates or return negative numbers, so don't we.
     637          30 :         if (nDate1 > nDate2)
     638             :         {
     639           2 :             PushIllegalArgument();
     640           2 :             return;
     641             :         }
     642             : 
     643          28 :         long dd = nDate2 - nDate1;
     644             :         // Zero difference or number of days can be returned immediately.
     645          28 :         if (dd == 0 || aInterval.equalsIgnoreAsciiCase( "d" ))
     646             :         {
     647           6 :             PushDouble( dd );
     648           6 :             return;
     649             :         }
     650             : 
     651             :         // split dates in day, month, year for use with formats other than "d"
     652             :         sal_uInt16 d1, m1, y1, d2, m2, y2;
     653          22 :         Date aDate1( *( pFormatter->GetNullDate()));
     654          22 :         aDate1 += (long) ::rtl::math::approxFloor( nDate1 );
     655          22 :         y1 = aDate1.GetYear();
     656          22 :         m1 = aDate1.GetMonth();
     657          22 :         d1 = aDate1.GetDay();
     658          22 :         Date aDate2( *( pFormatter->GetNullDate()));
     659          22 :         aDate2 += (long) ::rtl::math::approxFloor( nDate2 );
     660          22 :         y2 = aDate2.GetYear();
     661          22 :         m2 = aDate2.GetMonth();
     662          22 :         d2 = aDate2.GetDay();
     663             : 
     664          22 :         if (  aInterval.equalsIgnoreAsciiCase( "m" ) )
     665             :         {
     666             :             // Return number of months.
     667           8 :             int md = m2 - m1 + 12 * (y2 - y1);
     668           8 :             if (d1 > d2)
     669           0 :                 --md;
     670           8 :             PushInt( md );
     671             :         }
     672          14 :         else if ( aInterval.equalsIgnoreAsciiCase( "y" ) )
     673             :         {
     674             :             // Return number of years.
     675             :             int yd;
     676           2 :             if ( y2 > y1 )
     677             :             {
     678           0 :                 if (m2 > m1 || (m2 == m1 && d2 >= d1))
     679           0 :                     yd = y2 - y1;       // complete years between dates
     680             :                 else
     681           0 :                     yd = y2 - y1 - 1;   // one incomplete year
     682             :             }
     683             :             else
     684             :             {
     685             :                 // Year is equal as we don't allow reversed arguments, no
     686             :                 // complete year between dates.
     687           2 :                 yd = 0;
     688             :             }
     689           2 :             PushInt( yd );
     690             :         }
     691          12 :         else if ( aInterval.equalsIgnoreAsciiCase( "md" ) )
     692             :         {
     693             :             // Return number of days, excluding months and years.
     694             :             // This is actually the remainder of days when subtracting years
     695             :             // and months from the difference of dates. Birthday-like 23 years
     696             :             // and 10 months and 19 days.
     697             : 
     698             :             // Algorithm's roll-over behavior extracted from Excel by try and
     699             :             // error..
     700             :             // If day1 <= day2 then simply day2 - day1.
     701             :             // If day1 > day2 then set month1 to month2-1 and year1 to
     702             :             // year2(-1) and subtract dates, e.g. for 2012-01-28,2012-03-01 set
     703             :             // 2012-02-28 and then (2012-03-01)-(2012-02-28) => 2 days (leap
     704             :             // year).
     705             :             // For 2011-01-29,2011-03-01 the non-existent 2011-02-29 rolls over
     706             :             // to 2011-03-01 so the result is 0. Same for day 31 in months with
     707             :             // only 30 days.
     708             : 
     709             :             long nd;
     710           6 :             if (d1 <= d2)
     711           6 :                 nd = d2 - d1;
     712             :             else
     713             :             {
     714           0 :                 if (m2 == 1)
     715             :                 {
     716           0 :                     aDate1.SetYear( y2 - 1 );
     717           0 :                     aDate1.SetMonth( 12 );
     718             :                 }
     719             :                 else
     720             :                 {
     721           0 :                     aDate1.SetYear( y2 );
     722           0 :                     aDate1.SetMonth( m2 - 1 );
     723             :                 }
     724           0 :                 aDate1.Normalize();
     725           0 :                 nd = aDate2 - aDate1;
     726             :             }
     727           6 :             PushDouble( nd );
     728             :         }
     729           6 :         else if ( aInterval.equalsIgnoreAsciiCase( "ym" ) )
     730             :         {
     731             :             // Return number of months, excluding years.
     732           2 :             int md = m2 - m1 + 12 * (y2 - y1);
     733           2 :             if (d1 > d2)
     734           0 :                 --md;
     735           2 :             md %= 12;
     736           2 :             PushInt( md );
     737             :         }
     738           4 :         else if ( aInterval.equalsIgnoreAsciiCase( "yd" ) )
     739             :         {
     740             :             // Return number of days, excluding years.
     741             : 
     742             :             /* TODO: check what Excel really does, though this seems to be
     743             :              * reasonable */
     744             : 
     745             :             // Condition corresponds with "y".
     746           4 :             if (m2 > m1 || (m2 == m1 && d2 >= d1))
     747           4 :                 aDate1.SetYear( y2 );
     748             :             else
     749           0 :                 aDate1.SetYear( y2 - 1 );
     750             :                 // XXX NOTE: Excel for the case 1988-06-22,2012-05-11 returns
     751             :                 // 323, whereas the result here is 324. Don't they use the leap
     752             :                 // year of 2012?
     753             :                 // http://www.cpearson.com/excel/datedif.aspx "DATEDIF And Leap
     754             :                 // Years" is not correct and Excel 2010 correctly returns 0 in
     755             :                 // both cases mentioned there. Also using year1 as mentioned
     756             :                 // produces incorrect results in other cases and different from
     757             :                 // Excel 2010. Apparently they fixed some calculations.
     758           4 :             aDate1.Normalize();
     759           4 :             double nd = aDate2 - aDate1;
     760           4 :             PushDouble( nd );
     761             :         }
     762             :         else
     763           0 :             PushIllegalArgument();               // unsupported format
     764             :     }
     765             : }
     766             : 
     767           0 : void ScInterpreter::ScGetTimeValue()
     768             : {
     769           0 :     OUString aInputString = GetString().getString();
     770           0 :     sal_uInt32 nFIndex = 0;                 // damit default Land/Spr.
     771             :     double fVal;
     772           0 :     if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
     773             :     {
     774           0 :         short eType = pFormatter->GetType(nFIndex);
     775           0 :         if (eType == css::util::NumberFormat::TIME || eType == css::util::NumberFormat::DATETIME)
     776             :         {
     777           0 :             double fDateVal = rtl::math::approxFloor(fVal);
     778           0 :             double fTimeVal = fVal - fDateVal;
     779           0 :             PushDouble(fTimeVal);
     780             :         }
     781             :         else
     782           0 :             PushIllegalArgument();
     783             :     }
     784             :     else
     785           0 :         PushIllegalArgument();
     786           0 : }
     787             : 
     788           6 : void ScInterpreter::ScPlusMinus()
     789             : {
     790           6 :     double nVal = GetDouble();
     791           6 :     short n = 0;
     792           6 :     if (nVal < 0.0)
     793           3 :         n = -1;
     794           3 :     else if (nVal > 0.0)
     795           3 :         n = 1;
     796           6 :     PushInt( n );
     797           6 : }
     798             : 
     799           3 : void ScInterpreter::ScAbs()
     800             : {
     801           3 :     PushDouble(fabs(GetDouble()));
     802           3 : }
     803             : 
     804           6 : void ScInterpreter::ScInt()
     805             : {
     806           6 :     PushDouble(::rtl::math::approxFloor(GetDouble()));
     807           6 : }
     808             : 
     809        3052 : void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode )
     810             : {
     811        3052 :     sal_uInt8 nParamCount = GetByte();
     812        3052 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
     813             :     {
     814        3052 :         double fVal = 0.0;
     815        3052 :         if (nParamCount == 1)
     816           6 :             fVal = ::rtl::math::round( GetDouble(), 0, eMode );
     817             :         else
     818             :         {
     819        3046 :             sal_Int32 nDec = (sal_Int32) ::rtl::math::approxFloor(GetDouble());
     820        3046 :             if( nDec < -20 || nDec > 20 )
     821           0 :                 PushIllegalArgument();
     822             :             else
     823        3046 :                 fVal = ::rtl::math::round( GetDouble(), (short)nDec, eMode );
     824             :         }
     825        3052 :         PushDouble(fVal);
     826             :     }
     827        3052 : }
     828             : 
     829        3016 : void ScInterpreter::ScRound()
     830             : {
     831        3016 :     RoundNumber( rtl_math_RoundingMode_Corrected );
     832        3016 : }
     833             : 
     834          21 : void ScInterpreter::ScRoundDown()
     835             : {
     836          21 :     RoundNumber( rtl_math_RoundingMode_Down );
     837          21 : }
     838             : 
     839          15 : void ScInterpreter::ScRoundUp()
     840             : {
     841          15 :     RoundNumber( rtl_math_RoundingMode_Up );
     842          15 : }
     843             : 
     844             : /** fdo69552 ODFF1.2 function CEILING and Excel function CEILING.MATH
     845             :     In essence, the difference between the two is that ODFF-CEILING needs to
     846             :     have arguments value and significance of the same sign and with
     847             :     CEILING.MATH the sign of argument significance is irrevelevant.
     848             :     This is why ODFF-CEILING is exported to Excel as CEILING.MATH and
     849             :     CEILING.MATH is imported in Calc as CEILING.MATH
     850             :  */
     851           9 : void ScInterpreter::ScCeil( bool bODFF )
     852             : {
     853           9 :     sal_uInt8 nParamCount = GetByte();
     854           9 :     if ( MustHaveParamCount( nParamCount, 1, 3 ) )
     855             :     {
     856           9 :         bool bAbs = nParamCount == 3 && GetBool();
     857             :         double fDec, fVal;
     858           9 :         if ( nParamCount == 1 )
     859             :         {
     860           0 :             fVal = GetDouble();
     861           0 :             fDec = ( fVal < 0 ? -1 : 1 );
     862             :         }
     863             :         else
     864             :         {
     865           9 :             bool bArgumentMissing = IsMissing();
     866           9 :             fDec = GetDouble();
     867           9 :             fVal = GetDouble();
     868           9 :             if ( bArgumentMissing )
     869           0 :                 fDec = ( fVal < 0 ? -1 : 1 );
     870             :         }
     871           9 :         if ( fVal == 0 || fDec == 0.0 )
     872           0 :             PushInt( 0 );
     873             :         else
     874             :         {
     875           9 :             if ( bODFF && fVal * fDec < 0 )
     876           0 :                 PushIllegalArgument();
     877             :             else
     878             :             {
     879           9 :                 if ( fVal * fDec < 0.0 )
     880           0 :                     fDec = -fDec;
     881             : 
     882           9 :                 if ( !bAbs && fVal < 0.0 )
     883           6 :                     PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
     884             :                 else
     885           3 :                     PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
     886             :             }
     887             :         }
     888             :     }
     889           9 : }
     890             : 
     891             : // fdo69552 Excel function CEILING
     892           0 : void ScInterpreter::ScCeil_MS()
     893             : {
     894           0 :     sal_uInt8 nParamCount = GetByte();
     895           0 :     if ( MustHaveParamCount( nParamCount, 2 ) )
     896             :     {
     897           0 :         double fDec = GetDouble();
     898           0 :         double fVal = GetDouble();
     899           0 :         if ( fVal == 0 || fDec == 0.0 )
     900           0 :             PushInt(0);
     901           0 :         else if ( fVal > 0.0 && fDec < 0.0 )
     902           0 :             PushIllegalArgument();
     903             :         else
     904           0 :             PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
     905             :     }
     906           0 : }
     907             : 
     908             : // fdo69552 Excel functions CEILING.PRECISE and ISO.CEILING
     909          12 : void ScInterpreter::ScCeil_Precise()
     910             : {
     911          12 :     sal_uInt8 nParamCount = GetByte();
     912          12 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
     913             :     {
     914             :         double fDec, fVal;
     915          12 :         if ( nParamCount == 1 )
     916             :         {
     917           0 :             fVal = GetDouble();
     918           0 :             fDec = 1.0;
     919             :         }
     920             :         else
     921             :         {
     922          12 :             fDec = fabs( GetDoubleWithDefault( 1.0 ));
     923          12 :             fVal = GetDouble();
     924             :         }
     925          12 :         if ( fDec == 0.0 || fVal == 0.0 )
     926           0 :             PushInt( 0 );
     927             :         else
     928          12 :             PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
     929             :     }
     930          12 : }
     931             : 
     932           9 : void ScInterpreter::ScFloor()
     933             : {
     934           9 :     sal_uInt8 nParamCount = GetByte();
     935           9 :     if ( MustHaveParamCount( nParamCount, 2, 3 ) )
     936             :     {
     937           9 :         bool bAbs = nParamCount == 3 && GetBool();
     938           9 :         double fDec = GetDouble();
     939           9 :         double fVal = GetDouble();
     940           9 :         if ( fDec == 0.0 )
     941           0 :             PushInt(0);
     942           9 :         else if (fVal*fDec < 0.0)
     943           0 :             PushIllegalArgument();
     944             :         else
     945             :         {
     946           9 :             if ( !bAbs && fVal < 0.0 )
     947           6 :                 PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec);
     948             :             else
     949           3 :                 PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec);
     950             :         }
     951             :     }
     952           9 : }
     953             : 
     954           6 : void ScInterpreter::ScFloor_MS()
     955             : {
     956           6 :     sal_uInt8 nParamCount = GetByte();
     957           6 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
     958             :     {
     959             :         double fDec, fVal;
     960           6 :         if ( nParamCount == 1 )
     961             :         {
     962           0 :             fVal = GetDouble();
     963           0 :             fDec = 1.0;
     964             :         }
     965             :         else
     966             :         {
     967           6 :             fDec = fabs( GetDoubleWithDefault( 1.0 ));
     968           6 :             fVal = GetDouble();
     969             :         }
     970           6 :         if ( fDec == 0.0 || fVal == 0.0 )
     971           0 :             PushInt( 0 );
     972             :         else
     973           6 :             PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
     974             :     }
     975           6 : }
     976             : 
     977          18 : void ScInterpreter::ScEven()
     978             : {
     979          18 :     double fVal = GetDouble();
     980          18 :     if (fVal < 0.0)
     981           5 :         PushDouble(::rtl::math::approxFloor(fVal/2.0) * 2.0);
     982             :     else
     983          13 :         PushDouble(::rtl::math::approxCeil(fVal/2.0) * 2.0);
     984          18 : }
     985             : 
     986           6 : void ScInterpreter::ScOdd()
     987             : {
     988           6 :     double fVal = GetDouble();
     989           6 :     if (fVal >= 0.0)
     990             :     {
     991           4 :         fVal = ::rtl::math::approxCeil(fVal);
     992           4 :         if (fmod(fVal, 2.0) == 0.0)
     993           3 :             fVal += 1.0;
     994             :     }
     995             :     else
     996             :     {
     997           2 :         fVal = ::rtl::math::approxFloor(fVal);
     998           2 :         if (fmod(fVal, 2.0) == 0.0)
     999           1 :             fVal -= 1.0;
    1000             :     }
    1001           6 :     PushDouble(fVal);
    1002           6 : }
    1003             : 
    1004           6 : void ScInterpreter::ScArcTan2()
    1005             : {
    1006           6 :     if ( MustHaveParamCount( GetByte(), 2 ) )
    1007             :     {
    1008           6 :         double nVal2 = GetDouble();
    1009           6 :         double nVal1 = GetDouble();
    1010           6 :         PushDouble(atan2(nVal2, nVal1));
    1011             :     }
    1012           6 : }
    1013             : 
    1014           6 : void ScInterpreter::ScLog()
    1015             : {
    1016           6 :     sal_uInt8 nParamCount = GetByte();
    1017           6 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    1018             :     {
    1019             :         double nBase;
    1020           6 :         if (nParamCount == 2)
    1021           6 :             nBase = GetDouble();
    1022             :         else
    1023           0 :             nBase = 10.0;
    1024           6 :         double nVal = GetDouble();
    1025           6 :         if (nVal > 0.0 && nBase > 0.0 && nBase != 1.0)
    1026           6 :             PushDouble(log(nVal) / log(nBase));
    1027             :         else
    1028           0 :             PushIllegalArgument();
    1029             :     }
    1030           6 : }
    1031             : 
    1032           6 : void ScInterpreter::ScLn()
    1033             : {
    1034           6 :     double fVal = GetDouble();
    1035           6 :     if (fVal > 0.0)
    1036           6 :         PushDouble(log(fVal));
    1037             :     else
    1038           0 :         PushIllegalArgument();
    1039           6 : }
    1040             : 
    1041           3 : void ScInterpreter::ScLog10()
    1042             : {
    1043           3 :     double fVal = GetDouble();
    1044           3 :     if (fVal > 0.0)
    1045           3 :         PushDouble(log10(fVal));
    1046             :     else
    1047           0 :         PushIllegalArgument();
    1048           3 : }
    1049             : 
    1050           1 : void ScInterpreter::ScNPV()
    1051             : {
    1052           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1053           1 :     short nParamCount = GetByte();
    1054           1 :     if ( MustHaveParamCount( nParamCount, 2, 31 ) )
    1055             :     {
    1056           1 :         double nVal = 0.0;
    1057             :         //We turn the stack upside down!
    1058             :         FormulaToken* pTemp[ 31 ];
    1059           5 :         for( short i = 0; i < nParamCount; i++ )
    1060           4 :             pTemp[ i ] = pStack[ sp - i - 1 ];
    1061           1 :         memcpy( &pStack[ sp - nParamCount ], pTemp, nParamCount * sizeof( FormulaToken* ) );
    1062           1 :         if (nGlobalError == 0)
    1063             :         {
    1064           1 :             double  nCount = 1.0;
    1065           1 :             double  nInterest = GetDouble();
    1066           1 :             --nParamCount;
    1067           1 :             size_t nRefInList = 0;
    1068           1 :             ScRange aRange;
    1069           5 :             while (nParamCount-- > 0)
    1070             :             {
    1071           3 :                 switch (GetStackType())
    1072             :                 {
    1073             :                     case svDouble :
    1074             :                     {
    1075           3 :                         nVal += (GetDouble() / pow(1.0 + nInterest, (double)nCount));
    1076           3 :                         nCount++;
    1077             :                     }
    1078           3 :                     break;
    1079             :                     case svSingleRef :
    1080             :                     {
    1081           0 :                         ScAddress aAdr;
    1082           0 :                         PopSingleRef( aAdr );
    1083           0 :                         ScRefCellValue aCell;
    1084           0 :                         aCell.assign(*pDok, aAdr);
    1085           0 :                         if (!aCell.hasEmptyValue() && aCell.hasNumeric())
    1086             :                         {
    1087           0 :                             double nCellVal = GetCellValue(aAdr, aCell);
    1088           0 :                             nVal += (nCellVal / pow(1.0 + nInterest, (double)nCount));
    1089           0 :                             nCount++;
    1090           0 :                         }
    1091             :                     }
    1092           0 :                     break;
    1093             :                     case svDoubleRef :
    1094             :                     case svRefList :
    1095             :                     {
    1096           0 :                         sal_uInt16 nErr = 0;
    1097             :                         double nCellVal;
    1098           0 :                         PopDoubleRef( aRange, nParamCount, nRefInList);
    1099           0 :                         ScHorizontalValueIterator aValIter( pDok, aRange );
    1100           0 :                         while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr))
    1101             :                         {
    1102           0 :                             nVal += (nCellVal / pow(1.0 + nInterest, (double)nCount));
    1103           0 :                             nCount++;
    1104             :                         }
    1105           0 :                         if ( nErr != 0 )
    1106           0 :                             SetError(nErr);
    1107             :                     }
    1108           0 :                     break;
    1109           0 :                     default : SetError(errIllegalParameter); break;
    1110             :                 }
    1111             :             }
    1112             :         }
    1113           1 :         PushDouble(nVal);
    1114             :     }
    1115           1 : }
    1116             : 
    1117           1 : void ScInterpreter::ScIRR()
    1118             : {
    1119             :     double fEstimated;
    1120           1 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1121           1 :     sal_uInt8 nParamCount = GetByte();
    1122           1 :     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
    1123           0 :         return;
    1124           1 :     if (nParamCount == 2)
    1125           0 :         fEstimated = GetDouble();
    1126             :     else
    1127           1 :         fEstimated = 0.1;
    1128           1 :     sal_uInt16 sPos = sp;                       //memory the position of the stack
    1129           1 :     double fEps = 1.0;
    1130             :     double x, fValue;
    1131           1 :     if (fEstimated == -1.0)
    1132           0 :         x = 0.1;                            // default result for divion by zero
    1133             :     else
    1134           1 :         x = fEstimated;                   // startvalue
    1135           1 :     switch (GetStackType())
    1136             :     {
    1137             :         case svDoubleRef :
    1138           1 :         break;
    1139             :         default:
    1140             :         {
    1141           0 :             PushIllegalParameter();
    1142           0 :             return;
    1143             :         }
    1144             :     }
    1145           1 :     const sal_uInt16 nIterationsMax = 20;
    1146           1 :     sal_uInt16 nItCount = 0;
    1147           1 :     ScRange aRange;
    1148           5 :     while (fEps > SCdEpsilon && nItCount < nIterationsMax)
    1149             :     {                                       // Newtons method:
    1150           3 :         sp = sPos;                          // reset stack
    1151           3 :         double nCount = 0.0;
    1152           3 :         double fNom = 0.0;
    1153           3 :         double fDenom = 0.0;
    1154           3 :         sal_uInt16 nErr = 0;
    1155           3 :         PopDoubleRef( aRange );
    1156           3 :         ScValueIterator aValIter(pDok, aRange, mnSubTotalFlags);
    1157           3 :         if (aValIter.GetFirst(fValue, nErr))
    1158             :         {
    1159           3 :             fNom +=           fValue / pow(1.0+x,(double)nCount);
    1160           3 :             fDenom  += -nCount * fValue / pow(1.0+x,nCount+1.0);
    1161           3 :             nCount++;
    1162          15 :             while ((nErr == 0) && aValIter.GetNext(fValue, nErr))
    1163             :             {
    1164           9 :                 fNom +=           fValue / pow(1.0+x,(double)nCount);
    1165           9 :                 fDenom  += -nCount * fValue / pow(1.0+x,nCount+1.0);
    1166           9 :                 nCount++;
    1167             :             }
    1168           3 :             SetError(nErr);
    1169             :         }
    1170           3 :         double xNew = x - fNom / fDenom;  // x(i+1) = x(i)-f(x(i))/f'(x(i))
    1171           3 :         nItCount++;
    1172           3 :         fEps = fabs(xNew - x);
    1173           3 :         x = xNew;
    1174             :     }
    1175           1 :     if (fEstimated == 0.0 && fabs(x) < SCdEpsilon)
    1176           0 :         x = 0.0;                        // adjust to zero
    1177           1 :     if (fEps < SCdEpsilon)
    1178           1 :         PushDouble(x);
    1179             :     else
    1180           0 :         PushError( errNoConvergence);
    1181             : }
    1182             : 
    1183           1 : void ScInterpreter::ScMIRR()
    1184             : {   // range_of_values ; rate_invest ; rate_reinvest
    1185           1 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1186           1 :     if( MustHaveParamCount( GetByte(), 3 ) )
    1187             :     {
    1188           1 :         double fRate1_reinvest = GetDouble() + 1;
    1189           1 :         double fRate1_invest = GetDouble() + 1;
    1190             : 
    1191           1 :         ScRange aRange;
    1192           1 :         PopDoubleRef( aRange );
    1193             : 
    1194           1 :         if( nGlobalError )
    1195           0 :             PushError( nGlobalError);
    1196             :         else
    1197             :         {
    1198           1 :             double fNPV_reinvest = 0.0;
    1199           1 :             double fPow_reinvest = 1.0;
    1200           1 :             double fNPV_invest = 0.0;
    1201           1 :             double fPow_invest = 1.0;
    1202           1 :             ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags );
    1203             :             double fCellValue;
    1204           1 :             sal_uLong nCount = 0;
    1205           1 :             sal_uInt16 nIterError = 0;
    1206             : 
    1207           1 :             bool bLoop = aValIter.GetFirst( fCellValue, nIterError );
    1208           6 :             while( bLoop )
    1209             :             {
    1210           4 :                 if( fCellValue > 0.0 )          // reinvestments
    1211           3 :                     fNPV_reinvest += fCellValue * fPow_reinvest;
    1212           1 :                 else if( fCellValue < 0.0 )     // investments
    1213           1 :                     fNPV_invest += fCellValue * fPow_invest;
    1214           4 :                 fPow_reinvest /= fRate1_reinvest;
    1215           4 :                 fPow_invest /= fRate1_invest;
    1216           4 :                 nCount++;
    1217             : 
    1218           4 :                 bLoop = aValIter.GetNext( fCellValue, nIterError );
    1219             :             }
    1220           1 :             if( nIterError )
    1221           0 :                 PushError( nIterError );
    1222             :             else
    1223             :             {
    1224           1 :                 double fResult = -fNPV_reinvest / fNPV_invest;
    1225           1 :                 fResult *= pow( fRate1_reinvest, (double) nCount - 1 );
    1226           1 :                 fResult = pow( fResult, div( 1.0, (nCount - 1)) );
    1227           1 :                 PushDouble( fResult - 1.0 );
    1228             :             }
    1229             :         }
    1230             :     }
    1231           1 : }
    1232             : 
    1233           1 : void ScInterpreter::ScISPMT()
    1234             : {   // rate ; period ; total_periods ; invest
    1235           1 :     if( MustHaveParamCount( GetByte(), 4 ) )
    1236             :     {
    1237           1 :         double fInvest = GetDouble();
    1238           1 :         double fTotal = GetDouble();
    1239           1 :         double fPeriod = GetDouble();
    1240           1 :         double fRate = GetDouble();
    1241             : 
    1242           1 :         if( nGlobalError )
    1243           0 :             PushError( nGlobalError);
    1244             :         else
    1245           1 :             PushDouble( fInvest * fRate * (fPeriod / fTotal - 1.0) );
    1246             :     }
    1247           1 : }
    1248             : 
    1249             : // Finanzfunktionen
    1250           1 : double ScInterpreter::ScGetBw(double fInterest, double fZzr, double fRmz,
    1251             :                               double fZw, double fF)
    1252             : {
    1253             :     double fBw;
    1254           1 :     if (fInterest == 0.0)
    1255           0 :         fBw = fZw + fRmz * fZzr;
    1256           1 :     else if (fF > 0.0)
    1257           0 :         fBw = (fZw * pow(1.0 + fInterest, -fZzr))
    1258           0 :                 + (fRmz * (1.0 - pow(1.0 + fInterest, -fZzr + 1.0)) / fInterest)
    1259           0 :                 + fRmz;
    1260             :     else
    1261           1 :         fBw = (fZw * pow(1.0 + fInterest, -fZzr))
    1262           1 :                 + (fRmz * (1.0 - pow(1.0 + fInterest, -fZzr)) / fInterest);
    1263           1 :     return -fBw;
    1264             : }
    1265             : 
    1266           1 : void ScInterpreter::ScPV()
    1267             : {
    1268           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1269           1 :     double nRmz, nZzr, nInterest, nZw = 0, nFlag = 0;
    1270           1 :     sal_uInt8 nParamCount = GetByte();
    1271           1 :     if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
    1272           1 :         return;
    1273           1 :     if (nParamCount == 5)
    1274           0 :         nFlag = GetDouble();
    1275           1 :     if (nParamCount >= 4)
    1276           1 :         nZw   = GetDouble();
    1277           1 :     nRmz  = GetDouble();
    1278           1 :     nZzr  = GetDouble();
    1279           1 :     nInterest = GetDouble();
    1280           1 :     PushDouble(ScGetBw(nInterest, nZzr, nRmz, nZw, nFlag));
    1281             : }
    1282             : 
    1283           1 : void ScInterpreter::ScSYD()
    1284             : {
    1285           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1286           1 :     if ( MustHaveParamCount( GetByte(), 4 ) )
    1287             :     {
    1288           1 :         double nZr = GetDouble();
    1289           1 :         double nTimeLength = GetDouble();
    1290           1 :         double nRest = GetDouble();
    1291           1 :         double nValue = GetDouble();
    1292           1 :         double nDia = ((nValue - nRest) * (nTimeLength - nZr + 1.0)) /
    1293           1 :                       ((nTimeLength * (nTimeLength + 1.0)) / 2.0);
    1294           1 :         PushDouble(nDia);
    1295             :     }
    1296           1 : }
    1297             : 
    1298          21 : double ScInterpreter::ScGetGDA(double fValue, double fRest, double fTimeLength,
    1299             :                 double fPeriod, double fFactor)
    1300             : {
    1301             :     double fGda, fInterest, fOldValue, fNewValue;
    1302          21 :     fInterest = fFactor / fTimeLength;
    1303          21 :     if (fInterest >= 1.0)
    1304             :     {
    1305           0 :         fInterest = 1.0;
    1306           0 :         if (fPeriod == 1.0)
    1307           0 :             fOldValue = fValue;
    1308             :         else
    1309           0 :             fOldValue = 0.0;
    1310             :     }
    1311             :     else
    1312          21 :         fOldValue = fValue * pow(1.0 - fInterest, fPeriod - 1.0);
    1313          21 :     fNewValue = fValue * pow(1.0 - fInterest, fPeriod);
    1314             : 
    1315          21 :     if (fNewValue < fRest)
    1316           0 :         fGda = fOldValue - fRest;
    1317             :     else
    1318          21 :         fGda = fOldValue - fNewValue;
    1319          21 :     if (fGda < 0.0)
    1320           0 :         fGda = 0.0;
    1321          21 :     return fGda;
    1322             : }
    1323             : 
    1324           1 : void ScInterpreter::ScDDB()
    1325             : {
    1326           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1327           1 :     sal_uInt8 nParamCount = GetByte();
    1328           1 :     if ( MustHaveParamCount( nParamCount, 4, 5 ) )
    1329             :     {
    1330             :         double nFactor;
    1331           1 :         if (nParamCount == 5)
    1332           1 :             nFactor = GetDouble();
    1333             :         else
    1334           0 :             nFactor = 2.0;
    1335           1 :         double nPeriod = GetDouble();
    1336           1 :         double nTimeLength   = GetDouble();
    1337           1 :         double nRest    = GetDouble();
    1338           1 :         double nValue    = GetDouble();
    1339           1 :         if (nValue < 0.0 || nRest < 0.0 || nFactor <= 0.0 || nRest > nValue
    1340           1 :                         || nPeriod < 1.0 || nPeriod > nTimeLength)
    1341           0 :             PushIllegalArgument();
    1342             :         else
    1343           1 :             PushDouble(ScGetGDA(nValue, nRest, nTimeLength, nPeriod, nFactor));
    1344             :     }
    1345           1 : }
    1346             : 
    1347           1 : void ScInterpreter::ScDB()
    1348             : {
    1349           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1350           1 :     sal_uInt8 nParamCount = GetByte();
    1351           1 :     if ( !MustHaveParamCount( nParamCount, 4, 5 ) )
    1352           0 :         return ;
    1353             :     double nMonths;
    1354           1 :     if (nParamCount == 4)
    1355           0 :         nMonths = 12.0;
    1356             :     else
    1357           1 :         nMonths = ::rtl::math::approxFloor(GetDouble());
    1358           1 :     double nPeriod = GetDouble();
    1359           1 :     double nTimeLength = GetDouble();
    1360           1 :     double nRest = GetDouble();
    1361           1 :     double nValue = GetDouble();
    1362           2 :     if (nMonths < 1.0 || nMonths > 12.0 || nTimeLength > 1200.0 || nRest < 0.0 ||
    1363           2 :         nPeriod > (nTimeLength + 1.0) || nRest > nValue || nValue < 0.0)
    1364             :     {
    1365           0 :         PushIllegalArgument();
    1366           0 :         return;
    1367             :     }
    1368           1 :     double nOffRate = 1.0 - pow(nRest / nValue, 1.0 / nTimeLength);
    1369           1 :     nOffRate = ::rtl::math::approxFloor((nOffRate * 1000.0) + 0.5) / 1000.0;
    1370           1 :     double nFirstOffRate = nValue * nOffRate * nMonths / 12.0;
    1371           1 :     double nGda2 = 0.0;
    1372           1 :     if (::rtl::math::approxFloor(nPeriod) == 1)
    1373           1 :         nGda2 = nFirstOffRate;
    1374             :     else
    1375             :     {
    1376           0 :         double nSumOffRate = nFirstOffRate;
    1377           0 :         double nMin = nTimeLength;
    1378           0 :         if (nMin > nPeriod) nMin = nPeriod;
    1379           0 :         sal_uInt16 iMax = (sal_uInt16)::rtl::math::approxFloor(nMin);
    1380           0 :         for (sal_uInt16 i = 2; i <= iMax; i++)
    1381             :         {
    1382           0 :             nGda2 = (nValue - nSumOffRate) * nOffRate;
    1383           0 :             nSumOffRate += nGda2;
    1384             :         }
    1385           0 :         if (nPeriod > nTimeLength)
    1386           0 :             nGda2 = ((nValue - nSumOffRate) * nOffRate * (12.0 - nMonths)) / 12.0;
    1387             :     }
    1388           1 :     PushDouble(nGda2);
    1389             : }
    1390             : 
    1391           2 : double ScInterpreter::ScInterVDB(double fValue,double fRest,double fTimeLength,
    1392             :                              double fTimeLength1,double fPeriod,double fFactor)
    1393             : {
    1394           2 :     double fVdb=0;
    1395           2 :     double fIntEnd   = ::rtl::math::approxCeil(fPeriod);
    1396           2 :     sal_uLong nLoopEnd   = (sal_uLong) fIntEnd;
    1397             : 
    1398             :     double fTerm, fLia;
    1399           2 :     double fSalvageValue = fValue - fRest;
    1400           2 :     bool bNowLia = false;
    1401             : 
    1402             :     double fGda;
    1403             :     sal_uLong i;
    1404           2 :     fLia=0;
    1405          22 :     for ( i = 1; i <= nLoopEnd; i++)
    1406             :     {
    1407          20 :         if(!bNowLia)
    1408             :         {
    1409          20 :             fGda = ScGetGDA(fValue, fRest, fTimeLength, (double) i, fFactor);
    1410          20 :             fLia = fSalvageValue/ (fTimeLength1 - (double) (i-1));
    1411             : 
    1412          20 :             if (fLia > fGda)
    1413             :             {
    1414           0 :                 fTerm = fLia;
    1415           0 :                 bNowLia = true;
    1416             :             }
    1417             :             else
    1418             :             {
    1419          20 :                 fTerm = fGda;
    1420          20 :                 fSalvageValue -= fGda;
    1421             :             }
    1422             :         }
    1423             :         else
    1424             :         {
    1425           0 :             fTerm = fLia;
    1426             :         }
    1427             : 
    1428          20 :         if ( i == nLoopEnd)
    1429           2 :             fTerm *= ( fPeriod + 1.0 - fIntEnd );
    1430             : 
    1431          20 :         fVdb += fTerm;
    1432             :     }
    1433           2 :     return fVdb;
    1434             : }
    1435             : 
    1436           0 : inline double DblMin( double a, double b )
    1437             : {
    1438           0 :     return (a < b) ? a : b;
    1439             : }
    1440             : 
    1441           1 : void ScInterpreter::ScVDB()
    1442             : {
    1443           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1444           1 :     sal_uInt8 nParamCount = GetByte();
    1445           1 :     if ( MustHaveParamCount( nParamCount, 5, 7 ) )
    1446             :     {
    1447           1 :         double fValue, fRest, fTimeLength, fStart, fEnd, fFactor, fVdb = 0.0;
    1448             :         bool bFlag;
    1449           1 :         if (nParamCount == 7)
    1450           0 :             bFlag = GetBool();
    1451             :         else
    1452           1 :             bFlag = false;
    1453           1 :         if (nParamCount >= 6)
    1454           1 :             fFactor = GetDouble();
    1455             :         else
    1456           0 :             fFactor = 2.0;
    1457           1 :         fEnd   = GetDouble();
    1458           1 :         fStart = GetDouble();
    1459           1 :         fTimeLength  = GetDouble();
    1460           1 :         fRest   = GetDouble();
    1461           1 :         fValue   = GetDouble();
    1462           1 :         if (fStart < 0.0 || fEnd < fStart || fEnd > fTimeLength || fValue < 0.0
    1463           1 :                           || fRest > fValue || fFactor <= 0.0)
    1464           0 :             PushIllegalArgument();
    1465             :         else
    1466             :         {
    1467           1 :             double fIntStart = ::rtl::math::approxFloor(fStart);
    1468           1 :             double fIntEnd   = ::rtl::math::approxCeil(fEnd);
    1469           1 :             sal_uLong nLoopStart = (sal_uLong) fIntStart;
    1470           1 :             sal_uLong nLoopEnd   = (sal_uLong) fIntEnd;
    1471             : 
    1472           1 :             fVdb = 0.0;
    1473           1 :             if (bFlag)
    1474             :             {
    1475           0 :                 for (sal_uLong i = nLoopStart + 1; i <= nLoopEnd; i++)
    1476             :                 {
    1477           0 :                     double fTerm = ScGetGDA(fValue, fRest, fTimeLength, (double) i, fFactor);
    1478             : 
    1479             :                     //respect partial period in the Beginning/ End:
    1480           0 :                     if ( i == nLoopStart+1 )
    1481           0 :                         fTerm *= ( DblMin( fEnd, fIntStart + 1.0 ) - fStart );
    1482           0 :                     else if ( i == nLoopEnd )
    1483           0 :                         fTerm *= ( fEnd + 1.0 - fIntEnd );
    1484             : 
    1485           0 :                     fVdb += fTerm;
    1486             :                 }
    1487             :             }
    1488             :             else
    1489             :             {
    1490             : 
    1491           1 :                 double fTimeLength1=fTimeLength;
    1492             : 
    1493             :                 //@ The question of all questions: 'Is this right'
    1494           1 :                 if(!::rtl::math::approxEqual(fStart,::rtl::math::approxFloor(fStart)))
    1495             :                 {
    1496           0 :                     if(fFactor>1)
    1497             :                     {
    1498           0 :                         if(fStart>fTimeLength/2 || ::rtl::math::approxEqual(fStart,fTimeLength/2))
    1499             :                         {
    1500           0 :                             double fPart=fStart-fTimeLength/2;
    1501           0 :                             fStart=fTimeLength/2;
    1502           0 :                             fEnd-=fPart;
    1503           0 :                             fTimeLength1+=1;
    1504             :                         }
    1505             :                     }
    1506             :                 }
    1507             : 
    1508           1 :                 fValue-=ScInterVDB(fValue,fRest,fTimeLength,fTimeLength1,fStart,fFactor);
    1509           1 :                 fVdb=ScInterVDB(fValue,fRest,fTimeLength,fTimeLength-fStart,fEnd-fStart,fFactor);
    1510             :             }
    1511             :         }
    1512           1 :         PushDouble(fVdb);
    1513             :     }
    1514           1 : }
    1515             : 
    1516           1 : void ScInterpreter::ScDuration()
    1517             : {
    1518           1 :     if ( MustHaveParamCount( GetByte(), 3 ) )
    1519             :     {
    1520           1 :         double nFuture = GetDouble();
    1521           1 :         double nPresent = GetDouble();
    1522           1 :         double nInterest = GetDouble();
    1523           1 :         PushDouble(log(nFuture / nPresent) / boost::math::log1p(nInterest));
    1524             :     }
    1525           1 : }
    1526             : 
    1527           1 : void ScInterpreter::ScSLN()
    1528             : {
    1529           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1530           1 :     if ( MustHaveParamCount( GetByte(), 3 ) )
    1531             :     {
    1532           1 :         double nTimeLength = GetDouble();
    1533           1 :         double nRest = GetDouble();
    1534           1 :         double nValue = GetDouble();
    1535           1 :         PushDouble((nValue - nRest) / nTimeLength);
    1536             :     }
    1537           1 : }
    1538             : 
    1539           9 : double ScInterpreter::ScGetRmz(double fRate, double fNper, double fPv,
    1540             :                        double fFv, double fPaytype)
    1541             : {
    1542             :     double fPayment;
    1543           9 :     if (fRate == 0.0)
    1544           0 :         fPayment = (fPv + fFv) / fNper;
    1545             :     else
    1546             :     {
    1547           9 :         if (fPaytype > 0.0) // payment in advance
    1548           2 :             fPayment = (fFv + fPv * exp( fNper * ::rtl::math::log1p(fRate) ) ) * fRate /
    1549           2 :                 (::rtl::math::expm1( (fNper + 1) * ::rtl::math::log1p(fRate) ) - fRate);
    1550             :         else  // payment in arrear
    1551           7 :             fPayment = (fFv + fPv * exp(fNper * ::rtl::math::log1p(fRate) ) ) * fRate /
    1552           7 :                 ::rtl::math::expm1( fNper * ::rtl::math::log1p(fRate) );
    1553             :     }
    1554           9 :     return -fPayment;
    1555             : }
    1556             : 
    1557           1 : void ScInterpreter::ScPMT()
    1558             : {
    1559           1 :     double nInterest, nZzr, nBw, nZw = 0, nFlag = 0;
    1560           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1561           1 :     sal_uInt8 nParamCount = GetByte();
    1562           1 :     if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
    1563           1 :         return;
    1564           1 :     if (nParamCount == 5)
    1565           0 :         nFlag = GetDouble();
    1566           1 :     if (nParamCount >= 4)
    1567           0 :         nZw   = GetDouble();
    1568           1 :     nBw   = GetDouble();
    1569           1 :     nZzr  = GetDouble();
    1570           1 :     nInterest = GetDouble();
    1571           1 :     PushDouble(ScGetRmz(nInterest, nZzr, nBw, nZw, nFlag));
    1572             : }
    1573             : 
    1574           1 : void ScInterpreter::ScRRI()
    1575             : {
    1576           1 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1577           1 :     if ( MustHaveParamCount( GetByte(), 3 ) )
    1578             :     {
    1579           1 :         double nValueInFuture = GetDouble();
    1580           1 :         double nValueNow = GetDouble();
    1581           1 :         double nSpaceOfTime = GetDouble();
    1582           1 :         PushDouble(pow(nValueInFuture / nValueNow, 1.0 / nSpaceOfTime) - 1.0);
    1583             :     }
    1584           1 : }
    1585             : 
    1586          38 : double ScInterpreter::ScGetZw(double fInterest, double fZzr, double fRmz,
    1587             :                               double fBw, double fF)
    1588             : {
    1589             :     double fZw;
    1590          38 :     if (fInterest == 0.0)
    1591           0 :         fZw = fBw + fRmz * fZzr;
    1592             :     else
    1593             :     {
    1594          38 :         double fTerm = pow(1.0 + fInterest, fZzr);
    1595          38 :         if (fF > 0.0)
    1596           3 :             fZw = fBw * fTerm + fRmz*(1.0 + fInterest)*(fTerm - 1.0)/fInterest;
    1597             :         else
    1598          35 :             fZw = fBw * fTerm + fRmz*(fTerm - 1.0)/fInterest;
    1599             :     }
    1600          38 :     return -fZw;
    1601             : }
    1602             : 
    1603           1 : void ScInterpreter::ScFV()
    1604             : {
    1605           1 :     double nInterest, nZzr, nRmz, nBw = 0, nFlag = 0;
    1606           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1607           1 :     sal_uInt8 nParamCount = GetByte();
    1608           1 :     if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
    1609           1 :         return;
    1610           1 :     if (nParamCount == 5)
    1611           0 :         nFlag = GetDouble();
    1612           1 :     if (nParamCount >= 4)
    1613           1 :         nBw   = GetDouble();
    1614           1 :     nRmz  = GetDouble();
    1615           1 :     nZzr  = GetDouble();
    1616           1 :     nInterest = GetDouble();
    1617           1 :     PushDouble(ScGetZw(nInterest, nZzr, nRmz, nBw, nFlag));
    1618             : }
    1619             : 
    1620           1 : void ScInterpreter::ScNper()
    1621             : {
    1622           1 :     double nInterest, nRmz, nBw, nZw = 0, nFlag = 0;
    1623           1 :     sal_uInt8 nParamCount = GetByte();
    1624           1 :     if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
    1625           1 :         return;
    1626           1 :     if (nParamCount == 5)
    1627           0 :         nFlag = GetDouble();
    1628           1 :     if (nParamCount >= 4)
    1629           0 :         nZw   = GetDouble();
    1630           1 :     nBw   = GetDouble();
    1631           1 :     nRmz  = GetDouble();
    1632           1 :     nInterest = GetDouble();
    1633           1 :     if (nInterest == 0.0)
    1634           0 :         PushDouble(-(nBw + nZw)/nRmz);
    1635           1 :     else if (nFlag > 0.0)
    1636           0 :         PushDouble(log(-(nInterest*nZw-nRmz*(1.0+nInterest))/(nInterest*nBw+nRmz*(1.0+nInterest)))
    1637           0 :                   /boost::math::log1p(nInterest));
    1638             :     else
    1639           1 :         PushDouble(log(-(nInterest*nZw-nRmz)/(nInterest*nBw+nRmz))/boost::math::log1p(nInterest));
    1640             : }
    1641             : 
    1642           1 : bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
    1643             :                                    double fFv, double fPayType, double & fGuess )
    1644             : {
    1645             :     // See also #i15090#
    1646             :     // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i))
    1647             :     // This solution handles integer and non-integer values of Nper different.
    1648             :     // If ODFF will constraint Nper to integer, the distinction of cases can be
    1649             :     // removed; only the integer-part is needed then.
    1650           1 :     bool bValid = true, bFound = false;
    1651             :     double fX, fXnew, fTerm, fTermDerivation;
    1652             :     double fGeoSeries, fGeoSeriesDerivation;
    1653           1 :     const sal_uInt16 nIterationsMax = 150;
    1654           1 :     sal_uInt16 nCount = 0;
    1655           1 :     const double fEpsilonSmall = 1.0E-14;
    1656             :     // convert any fPayType situation to fPayType == zero situation
    1657           1 :     fFv = fFv - fPayment * fPayType;
    1658           1 :     fPv = fPv + fPayment * fPayType;
    1659           1 :     if (fNper == ::rtl::math::round( fNper, 0, rtl_math_RoundingMode_Corrected ))
    1660             :     { // Nper is an integer value
    1661           1 :         fX = fGuess;
    1662             :         double fPowN, fPowNminus1;  // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
    1663          11 :         while (!bFound && nCount < nIterationsMax)
    1664             :         {
    1665           9 :             fPowNminus1 = pow( 1.0+fX, fNper-1.0);
    1666           9 :             fPowN = fPowNminus1 * (1.0+fX);
    1667           9 :             if (rtl::math::approxEqual( fabs(fX), 0.0))
    1668             :             {
    1669           0 :                 fGeoSeries = fNper;
    1670           0 :                 fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
    1671             :             }
    1672             :             else
    1673             :             {
    1674           9 :                 fGeoSeries = (fPowN-1.0)/fX;
    1675           9 :                 fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;
    1676             :             }
    1677           9 :             fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;
    1678           9 :             fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation;
    1679           9 :             if (fabs(fTerm) < fEpsilonSmall)
    1680           0 :                 bFound = true;  // will catch root which is at an extreme
    1681             :             else
    1682             :             {
    1683           9 :                 if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
    1684           0 :                     fXnew = fX + 1.1 * SCdEpsilon;  // move away from zero slope
    1685             :                 else
    1686           9 :                     fXnew = fX - fTerm / fTermDerivation;
    1687           9 :                 nCount++;
    1688             :                 // more accuracy not possible in oscillating cases
    1689           9 :                 bFound = (fabs(fXnew - fX) < SCdEpsilon);
    1690           9 :                 fX = fXnew;
    1691             :             }
    1692             :         }
    1693             :         // Gnumeric returns roots < -1, Excel gives an error in that cases,
    1694             :         // ODFF says nothing about it. Enable the statement, if you want Excel's
    1695             :         // behavior.
    1696             :         //bValid =(fX >=-1.0);
    1697             :         // Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1
    1698             :         // anymore.
    1699           1 :         bValid = (fX > -1.0);
    1700             :     }
    1701             :     else
    1702             :     { // Nper is not an integer value.
    1703           0 :         fX = (fGuess < -1.0) ? -1.0 : fGuess;   // start with a valid fX
    1704           0 :         while (bValid && !bFound && nCount < nIterationsMax)
    1705             :         {
    1706           0 :             if (rtl::math::approxEqual( fabs(fX), 0.0))
    1707             :             {
    1708           0 :                 fGeoSeries = fNper;
    1709           0 :                 fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
    1710             :             }
    1711             :             else
    1712             :             {
    1713           0 :                 fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;
    1714           0 :                 fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;
    1715             :             }
    1716           0 :             fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;
    1717           0 :             fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation;
    1718           0 :             if (fabs(fTerm) < fEpsilonSmall)
    1719           0 :                 bFound = true;  // will catch root which is at an extreme
    1720             :             else
    1721             :             {
    1722           0 :                 if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0))
    1723           0 :                     fXnew = fX + 1.1 * SCdEpsilon;  // move away from zero slope
    1724             :                 else
    1725           0 :                     fXnew = fX - fTerm / fTermDerivation;
    1726           0 :                 nCount++;
    1727             :                 // more accuracy not possible in oscillating cases
    1728           0 :                 bFound = (fabs(fXnew - fX) < SCdEpsilon);
    1729           0 :                 fX = fXnew;
    1730           0 :                 bValid = (fX >= -1.0);  // otherwise pow(1.0+fX,fNper) will fail
    1731             :             }
    1732             :         }
    1733             :     }
    1734           1 :     fGuess = fX;    // return approximate root
    1735           1 :     return bValid && bFound;
    1736             : }
    1737             : 
    1738             : // In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess)
    1739           1 : void ScInterpreter::ScRate()
    1740             : {
    1741             :     double fPv, fPayment, fNper;
    1742             :     // defaults for missing arguments, see ODFF spec
    1743           1 :     double fFv = 0, fPayType = 0, fGuess = 0.1, fOrigGuess = 0.1;
    1744           1 :     bool bValid = true;
    1745           1 :     bool bDefaultGuess = true;
    1746           1 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1747           1 :     sal_uInt8 nParamCount = GetByte();
    1748           1 :     if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
    1749           0 :         return;
    1750           1 :     if (nParamCount == 6)
    1751             :     {
    1752           0 :         fOrigGuess = fGuess = GetDouble();
    1753           0 :         bDefaultGuess = false;
    1754             :     }
    1755           1 :     if (nParamCount >= 5)
    1756           0 :         fPayType = GetDouble();
    1757           1 :     if (nParamCount >= 4)
    1758           0 :         fFv = GetDouble();
    1759           1 :     fPv = GetDouble();
    1760           1 :     fPayment = GetDouble();
    1761           1 :     fNper = GetDouble();
    1762           1 :     if (fNper <= 0.0) // constraint from ODFF spec
    1763             :     {
    1764           0 :         PushIllegalArgument();
    1765           0 :         return;
    1766             :     }
    1767             :     // other values for fPayType might be meaningful,
    1768             :     // ODFF spec is not clear yet, enable statement if you want only 0 and 1
    1769             :     //if (fPayType != 0.0) fPayType = 1.0;
    1770           1 :     bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess);
    1771           1 :     if (!bValid)
    1772             :     {
    1773             :         /* TODO: try also for specified guess values, not only default? As is,
    1774             :          * a specified 0.1 guess may be error result but a default 0.1 guess
    1775             :          * may succeed. On the other hand, using a different guess value than
    1776             :          * the specified one may not be desired, even if that didn't match. */
    1777           0 :         if (bDefaultGuess)
    1778             :         {
    1779             :             /* TODO: this is rather ugly, instead of looping over different
    1780             :              * guess values and doing a Newton goal seek for each we could
    1781             :              * first insert the values into the RATE equation to obtain a set
    1782             :              * of y values and then do a bisecting goal seek, possibly using
    1783             :              * different algorithms. */
    1784           0 :             double fX = fOrigGuess;
    1785           0 :             for (int nStep = 2; nStep <= 10 && !bValid; ++nStep)
    1786             :             {
    1787           0 :                 fGuess = fX * nStep;
    1788           0 :                 bValid = RateIteration( fNper, fPayment, fPv, fFv, fPayType, fGuess);
    1789           0 :                 if (!bValid)
    1790             :                 {
    1791           0 :                     fGuess = fX / nStep;
    1792           0 :                     bValid = RateIteration( fNper, fPayment, fPv, fFv, fPayType, fGuess);
    1793             :                 }
    1794             :             }
    1795             :         }
    1796           0 :         if (!bValid)
    1797           0 :             SetError(errNoConvergence);
    1798             :     }
    1799           1 :     PushDouble(fGuess);
    1800             : }
    1801             : 
    1802           2 : double ScInterpreter::ScGetCompoundInterest(double fInterest, double fZr, double fZzr, double fBw,
    1803             :                                  double fZw, double fF, double& fRmz)
    1804             : {
    1805           2 :     fRmz = ScGetRmz(fInterest, fZzr, fBw, fZw, fF);     // fuer kapz auch bei fZr == 1
    1806             :     double fCompoundInterest;
    1807           2 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1808           2 :     if (fZr == 1.0)
    1809             :     {
    1810           1 :         if (fF > 0.0)
    1811           1 :             fCompoundInterest = 0.0;
    1812             :         else
    1813           0 :             fCompoundInterest = -fBw;
    1814             :     }
    1815             :     else
    1816             :     {
    1817           1 :         if (fF > 0.0)
    1818           0 :             fCompoundInterest = ScGetZw(fInterest, fZr-2.0, fRmz, fBw, 1.0) - fRmz;
    1819             :         else
    1820           1 :             fCompoundInterest = ScGetZw(fInterest, fZr-1.0, fRmz, fBw, 0.0);
    1821             :     }
    1822           2 :     return fCompoundInterest * fInterest;
    1823             : }
    1824             : 
    1825           1 : void ScInterpreter::ScIpmt()
    1826             : {
    1827           1 :     double nInterest, nZr, nZzr, nBw, nZw = 0, nFlag = 0;
    1828           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1829           1 :     sal_uInt8 nParamCount = GetByte();
    1830           1 :     if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
    1831           1 :         return;
    1832           1 :     if (nParamCount == 6)
    1833           0 :         nFlag = GetDouble();
    1834           1 :     if (nParamCount >= 5)
    1835           0 :         nZw   = GetDouble();
    1836           1 :     nBw   = GetDouble();
    1837           1 :     nZzr  = GetDouble();
    1838           1 :     nZr   = GetDouble();
    1839           1 :     nInterest = GetDouble();
    1840           1 :     if (nZr < 1.0 || nZr > nZzr)
    1841           0 :         PushIllegalArgument();
    1842             :     else
    1843             :     {
    1844             :         double nRmz;
    1845           1 :         PushDouble(ScGetCompoundInterest(nInterest, nZr, nZzr, nBw, nZw, nFlag, nRmz));
    1846             :     }
    1847             : }
    1848             : 
    1849           1 : void ScInterpreter::ScPpmt()
    1850             : {
    1851           1 :     double nInterest, nZr, nZzr, nBw, nZw = 0, nFlag = 0;
    1852           1 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1853           1 :     sal_uInt8 nParamCount = GetByte();
    1854           1 :     if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
    1855           1 :         return;
    1856           1 :     if (nParamCount == 6)
    1857           1 :         nFlag = GetDouble();
    1858           1 :     if (nParamCount >= 5)
    1859           1 :         nZw   = GetDouble();
    1860           1 :     nBw   = GetDouble();
    1861           1 :     nZzr  = GetDouble();
    1862           1 :     nZr   = GetDouble();
    1863           1 :     nInterest = GetDouble();
    1864           1 :     if (nZr < 1.0 || nZr > nZzr)
    1865           0 :         PushIllegalArgument();
    1866             :     else
    1867             :     {
    1868             :         double nRmz;
    1869           1 :         double nInterestz = ScGetCompoundInterest(nInterest, nZr, nZzr, nBw, nZw, nFlag, nRmz);
    1870           1 :         PushDouble(nRmz - nInterestz);
    1871             :     }
    1872             : }
    1873             : 
    1874           3 : void ScInterpreter::ScCumIpmt()
    1875             : {
    1876           3 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1877           3 :     if ( MustHaveParamCount( GetByte(), 6 ) )
    1878             :     {
    1879             :         double fInterest, fZzr, fBw, fStart, fEnd, fF;
    1880           3 :         fF      = GetDouble();
    1881           3 :         fEnd   = ::rtl::math::approxFloor(GetDouble());
    1882           3 :         fStart = ::rtl::math::approxFloor(GetDouble());
    1883           3 :         fBw     = GetDouble();
    1884           3 :         fZzr    = GetDouble();
    1885           3 :         fInterest   = GetDouble();
    1886           3 :         if (fStart < 1.0 || fEnd < fStart || fInterest <= 0.0 ||
    1887           3 :             fEnd > fZzr  || fZzr <= 0.0 || fBw <= 0.0)
    1888           0 :             PushIllegalArgument();
    1889             :         else
    1890             :         {
    1891           3 :             sal_uLong nStart = (sal_uLong) fStart;
    1892           3 :             sal_uLong nEnd = (sal_uLong) fEnd ;
    1893           3 :             double fRmz = ScGetRmz(fInterest, fZzr, fBw, 0.0, fF);
    1894           3 :             double fCompoundInterest = 0.0;
    1895           3 :             if (nStart == 1)
    1896             :             {
    1897           1 :                 if (fF <= 0.0)
    1898           1 :                     fCompoundInterest = -fBw;
    1899           1 :                 nStart++;
    1900             :             }
    1901          18 :             for (sal_uLong i = nStart; i <= nEnd; i++)
    1902             :             {
    1903          15 :                 if (fF > 0.0)
    1904           3 :                     fCompoundInterest += ScGetZw(fInterest, (double)(i-2), fRmz, fBw, 1.0) - fRmz;
    1905             :                 else
    1906          12 :                     fCompoundInterest += ScGetZw(fInterest, (double)(i-1), fRmz, fBw, 0.0);
    1907             :             }
    1908           3 :             fCompoundInterest *= fInterest;
    1909           3 :             PushDouble(fCompoundInterest);
    1910             :         }
    1911             :     }
    1912           3 : }
    1913             : 
    1914           3 : void ScInterpreter::ScCumPrinc()
    1915             : {
    1916           3 :     nFuncFmtType = css::util::NumberFormat::CURRENCY;
    1917           3 :     if ( MustHaveParamCount( GetByte(), 6 ) )
    1918             :     {
    1919             :         double fInterest, fZzr, fBw, fStart, fEnd, fF;
    1920           3 :         fF      = GetDouble();
    1921           3 :         fEnd   = ::rtl::math::approxFloor(GetDouble());
    1922           3 :         fStart = ::rtl::math::approxFloor(GetDouble());
    1923           3 :         fBw     = GetDouble();
    1924           3 :         fZzr    = GetDouble();
    1925           3 :         fInterest   = GetDouble();
    1926           3 :         if (fStart < 1.0 || fEnd < fStart || fInterest <= 0.0 ||
    1927           3 :             fEnd > fZzr  || fZzr <= 0.0 || fBw <= 0.0)
    1928           0 :             PushIllegalArgument();
    1929             :         else
    1930             :         {
    1931           3 :             double fRmz = ScGetRmz(fInterest, fZzr, fBw, 0.0, fF);
    1932           3 :             double fKapZ = 0.0;
    1933           3 :             sal_uLong nStart = (sal_uLong) fStart;
    1934           3 :             sal_uLong nEnd = (sal_uLong) fEnd;
    1935           3 :             if (nStart == 1)
    1936             :             {
    1937           1 :                 if (fF <= 0.0)
    1938           1 :                     fKapZ = fRmz + fBw * fInterest;
    1939             :                 else
    1940           0 :                     fKapZ = fRmz;
    1941           1 :                 nStart++;
    1942             :             }
    1943          24 :             for (sal_uLong i = nStart; i <= nEnd; i++)
    1944             :             {
    1945          21 :                 if (fF > 0.0)
    1946           0 :                     fKapZ += fRmz - (ScGetZw(fInterest, (double)(i-2), fRmz, fBw, 1.0) - fRmz) * fInterest;
    1947             :                 else
    1948          21 :                     fKapZ += fRmz - ScGetZw(fInterest, (double)(i-1), fRmz, fBw, 0.0) * fInterest;
    1949             :             }
    1950           3 :             PushDouble(fKapZ);
    1951             :         }
    1952             :     }
    1953           3 : }
    1954             : 
    1955           2 : void ScInterpreter::ScEffective()
    1956             : {
    1957           2 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1958           2 :     if ( MustHaveParamCount( GetByte(), 2 ) )
    1959             :     {
    1960           2 :         double fPeriods = GetDouble();
    1961           2 :         double fNominal = GetDouble();
    1962           2 :         if (fPeriods < 1.0 || fNominal <= 0.0)
    1963           0 :             PushIllegalArgument();
    1964             :         else
    1965             :         {
    1966           2 :             fPeriods = ::rtl::math::approxFloor(fPeriods);
    1967           2 :             PushDouble(pow(1.0 + fNominal/fPeriods, fPeriods) - 1.0);
    1968             :         }
    1969             :     }
    1970           2 : }
    1971             : 
    1972           2 : void ScInterpreter::ScNominal()
    1973             : {
    1974           2 :     nFuncFmtType = css::util::NumberFormat::PERCENT;
    1975           2 :     if ( MustHaveParamCount( GetByte(), 2 ) )
    1976             :     {
    1977           2 :         double fPeriods = GetDouble();
    1978           2 :         double fEffective = GetDouble();
    1979           2 :         if (fPeriods < 1.0 || fEffective <= 0.0)
    1980           0 :             PushIllegalArgument();
    1981             :         else
    1982             :         {
    1983           2 :             fPeriods = ::rtl::math::approxFloor(fPeriods);
    1984           2 :             PushDouble( (pow(fEffective + 1.0, 1.0 / fPeriods) - 1.0) * fPeriods );
    1985             :         }
    1986             :     }
    1987           2 : }
    1988             : 
    1989          10 : void ScInterpreter::ScMod()
    1990             : {
    1991          10 :     if ( MustHaveParamCount( GetByte(), 2 ) )
    1992             :     {
    1993          10 :         double fVal2 = GetDouble(); // Denominator
    1994          10 :         double fVal1 = GetDouble(); // Numerator
    1995          10 :         if (fVal2 == floor(fVal2))  // a pure integral number stored in double
    1996             :         {
    1997           7 :             double fResult = fmod(fVal1,fVal2);
    1998           7 :             if ( (fResult != 0.0) &&
    1999           6 :                 ((fVal1 > 0.0 && fVal2 < 0.0) || (fVal1 < 0.0 && fVal2 > 0.0)))
    2000           0 :                 fResult += fVal2 ;
    2001           7 :             PushDouble( fResult );
    2002             :         }
    2003             :         else
    2004             :         {
    2005             :             PushDouble( ::rtl::math::approxSub( fVal1,
    2006           3 :                     ::rtl::math::approxFloor(fVal1 / fVal2) * fVal2));
    2007             :         }
    2008             :     }
    2009          10 : }
    2010             : 
    2011           0 : void ScInterpreter::ScIntersect()
    2012             : {
    2013           0 :     formula::FormulaTokenRef p2nd = PopToken();
    2014           0 :     formula::FormulaTokenRef p1st = PopToken();
    2015             : 
    2016           0 :     if (nGlobalError || !p2nd || !p1st)
    2017             :     {
    2018           0 :         PushIllegalArgument();
    2019           0 :         return;
    2020             :     }
    2021             : 
    2022           0 :     StackVar sv1 = p1st->GetType();
    2023           0 :     StackVar sv2 = p2nd->GetType();
    2024           0 :     if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
    2025           0 :         (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
    2026             :     {
    2027           0 :         PushIllegalArgument();
    2028           0 :         return;
    2029             :     }
    2030             : 
    2031           0 :     formula::FormulaToken* x1 = p1st.get();
    2032           0 :     formula::FormulaToken* x2 = p2nd.get();
    2033           0 :     if (sv1 == svRefList || sv2 == svRefList)
    2034             :     {
    2035             :         // Now this is a bit nasty but it simplifies things, and having
    2036             :         // intersections with lists isn't too common, if at all..
    2037             :         // Convert a reference to list.
    2038           0 :         formula::FormulaToken* xt[2] = { x1, x2 };
    2039           0 :         StackVar sv[2] = { sv1, sv2 };
    2040           0 :         for (size_t i=0; i<2; ++i)
    2041             :         {
    2042           0 :             if (sv[i] == svSingleRef)
    2043             :             {
    2044             :                 ScComplexRefData aRef;
    2045           0 :                 aRef.Ref1 = aRef.Ref2 = *xt[i]->GetSingleRef();
    2046           0 :                 xt[i] = new ScRefListToken;
    2047           0 :                 xt[i]->GetRefList()->push_back( aRef);
    2048             :             }
    2049           0 :             else if (sv[i] == svDoubleRef)
    2050             :             {
    2051           0 :                 ScComplexRefData aRef = *xt[i]->GetDoubleRef();
    2052           0 :                 xt[i] = new ScRefListToken;
    2053           0 :                 xt[i]->GetRefList()->push_back( aRef);
    2054             :             }
    2055             :         }
    2056           0 :         x1 = xt[0], x2 = xt[1];
    2057             : 
    2058           0 :         ScTokenRef xRes = new ScRefListToken;
    2059           0 :         ScRefList* pRefList = xRes->GetRefList();
    2060           0 :         ScRefList::const_iterator end1( x1->GetRefList()->end());
    2061           0 :         ScRefList::const_iterator end2( x2->GetRefList()->end());
    2062           0 :         for (ScRefList::const_iterator it1( x1->GetRefList()->begin());
    2063             :                 it1 != end1; ++it1)
    2064             :         {
    2065           0 :             const ScAddress& r11 = (*it1).Ref1.toAbs(aPos);
    2066           0 :             const ScAddress& r12 = (*it1).Ref2.toAbs(aPos);
    2067           0 :             for (ScRefList::const_iterator it2( x2->GetRefList()->begin());
    2068             :                     it2 != end2; ++it2)
    2069             :             {
    2070           0 :                 const ScAddress& r21 = (*it2).Ref1.toAbs(aPos);
    2071           0 :                 const ScAddress& r22 = (*it2).Ref2.toAbs(aPos);
    2072           0 :                 SCCOL nCol1 = ::std::max( r11.Col(), r21.Col());
    2073           0 :                 SCROW nRow1 = ::std::max( r11.Row(), r21.Row());
    2074           0 :                 SCTAB nTab1 = ::std::max( r11.Tab(), r21.Tab());
    2075           0 :                 SCCOL nCol2 = ::std::min( r12.Col(), r22.Col());
    2076           0 :                 SCROW nRow2 = ::std::min( r12.Row(), r22.Row());
    2077           0 :                 SCTAB nTab2 = ::std::min( r12.Tab(), r22.Tab());
    2078           0 :                 if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
    2079             :                     ;   // nothing
    2080             :                 else
    2081             :                 {
    2082             :                     ScComplexRefData aRef;
    2083           0 :                     aRef.InitRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
    2084           0 :                     pRefList->push_back( aRef);
    2085             :                 }
    2086             :             }
    2087             :         }
    2088           0 :         size_t n = pRefList->size();
    2089           0 :         if (!n)
    2090           0 :             PushError( errNoRef);
    2091           0 :         else if (n == 1)
    2092             :         {
    2093           0 :             const ScComplexRefData& rRef = (*pRefList)[0];
    2094           0 :             if (rRef.Ref1 == rRef.Ref2)
    2095           0 :                 PushTempToken( new ScSingleRefToken( rRef.Ref1));
    2096             :             else
    2097           0 :                 PushTempToken( new ScDoubleRefToken( rRef));
    2098             :         }
    2099             :         else
    2100           0 :             PushTempToken( xRes.get());
    2101             :     }
    2102             :     else
    2103             :     {
    2104           0 :         formula::FormulaToken* pt[2] = { x1, x2 };
    2105           0 :         StackVar sv[2] = { sv1, sv2 };
    2106             :         SCCOL nC1[2], nC2[2];
    2107             :         SCROW nR1[2], nR2[2];
    2108             :         SCTAB nT1[2], nT2[2];
    2109           0 :         for (size_t i=0; i<2; ++i)
    2110             :         {
    2111           0 :             switch (sv[i])
    2112             :             {
    2113             :                 case svSingleRef:
    2114             :                 case svDoubleRef:
    2115             :                 {
    2116             :                     {
    2117           0 :                         const ScAddress& r = pt[i]->GetSingleRef()->toAbs(aPos);
    2118           0 :                         nC1[i] = r.Col();
    2119           0 :                         nR1[i] = r.Row();
    2120           0 :                         nT1[i] = r.Tab();
    2121             :                     }
    2122           0 :                     if (sv[i] == svDoubleRef)
    2123             :                     {
    2124           0 :                         const ScAddress& r = pt[i]->GetSingleRef2()->toAbs(aPos);
    2125           0 :                         nC2[i] = r.Col();
    2126           0 :                         nR2[i] = r.Row();
    2127           0 :                         nT2[i] = r.Tab();
    2128             :                     }
    2129             :                     else
    2130             :                     {
    2131           0 :                         nC2[i] = nC1[i];
    2132           0 :                         nR2[i] = nR1[i];
    2133           0 :                         nT2[i] = nT1[i];
    2134             :                     }
    2135             :                 }
    2136           0 :                 break;
    2137             :                 default:
    2138             :                     ;   // nothing, prevent compiler warning
    2139             :             }
    2140             :         }
    2141           0 :         SCCOL nCol1 = ::std::max( nC1[0], nC1[1]);
    2142           0 :         SCROW nRow1 = ::std::max( nR1[0], nR1[1]);
    2143           0 :         SCTAB nTab1 = ::std::max( nT1[0], nT1[1]);
    2144           0 :         SCCOL nCol2 = ::std::min( nC2[0], nC2[1]);
    2145           0 :         SCROW nRow2 = ::std::min( nR2[0], nR2[1]);
    2146           0 :         SCTAB nTab2 = ::std::min( nT2[0], nT2[1]);
    2147           0 :         if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
    2148           0 :             PushError( errNoRef);
    2149           0 :         else if (nCol2 == nCol1 && nRow2 == nRow1 && nTab2 == nTab1)
    2150           0 :             PushSingleRef( nCol1, nRow1, nTab1);
    2151             :         else
    2152           0 :             PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
    2153           0 :     }
    2154             : }
    2155             : 
    2156           0 : void ScInterpreter::ScRangeFunc()
    2157             : {
    2158           0 :     formula::FormulaTokenRef x2 = PopToken();
    2159           0 :     formula::FormulaTokenRef x1 = PopToken();
    2160             : 
    2161           0 :     if (nGlobalError || !x2 || !x1)
    2162             :     {
    2163           0 :         PushIllegalArgument();
    2164           0 :         return;
    2165             :     }
    2166           0 :     FormulaTokenRef xRes = extendRangeReference( *x1, *x2, aPos, false);
    2167           0 :     if (!xRes)
    2168           0 :         PushIllegalArgument();
    2169             :     else
    2170           0 :         PushTempToken( xRes.get());
    2171             : }
    2172             : 
    2173           6 : void ScInterpreter::ScUnionFunc()
    2174             : {
    2175           6 :     formula::FormulaTokenRef p2nd = PopToken();
    2176          12 :     formula::FormulaTokenRef p1st = PopToken();
    2177             : 
    2178           6 :     if (nGlobalError || !p2nd || !p1st)
    2179             :     {
    2180           0 :         PushIllegalArgument();
    2181           0 :         return;
    2182             :     }
    2183             : 
    2184           6 :     StackVar sv1 = p1st->GetType();
    2185           6 :     StackVar sv2 = p2nd->GetType();
    2186           6 :     if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
    2187           0 :         (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
    2188             :     {
    2189           0 :         PushIllegalArgument();
    2190           0 :         return;
    2191             :     }
    2192             : 
    2193           6 :     formula::FormulaToken* x1 = p1st.get();
    2194           6 :     formula::FormulaToken* x2 = p2nd.get();
    2195             : 
    2196          12 :     ScTokenRef xRes;
    2197             :     // Append to an existing RefList if there is one.
    2198           6 :     if (sv1 == svRefList)
    2199             :     {
    2200           3 :         xRes = x1;
    2201           3 :         sv1 = svUnknown;    // mark as handled
    2202             :     }
    2203           3 :     else if (sv2 == svRefList)
    2204             :     {
    2205           0 :         xRes = x2;
    2206           0 :         sv2 = svUnknown;    // mark as handled
    2207             :     }
    2208             :     else
    2209           3 :         xRes = new ScRefListToken;
    2210           6 :     ScRefList* pRes = xRes->GetRefList();
    2211           6 :     formula::FormulaToken* pt[2] = { x1, x2 };
    2212           6 :     StackVar sv[2] = { sv1, sv2 };
    2213          18 :     for (size_t i=0; i<2; ++i)
    2214             :     {
    2215          12 :         if (pt[i] == xRes)
    2216           3 :             continue;
    2217           9 :         switch (sv[i])
    2218             :         {
    2219             :             case svSingleRef:
    2220             :                 {
    2221             :                     ScComplexRefData aRef;
    2222           6 :                     aRef.Ref1 = aRef.Ref2 = *pt[i]->GetSingleRef();
    2223           6 :                     pRes->push_back( aRef);
    2224             :                 }
    2225           6 :                 break;
    2226             :             case svDoubleRef:
    2227           3 :                 pRes->push_back( *pt[i]->GetDoubleRef());
    2228           3 :                 break;
    2229             :             case svRefList:
    2230             :                 {
    2231           0 :                     const ScRefList* p = pt[i]->GetRefList();
    2232           0 :                     ScRefList::const_iterator it( p->begin());
    2233           0 :                     ScRefList::const_iterator end( p->end());
    2234           0 :                     for ( ; it != end; ++it)
    2235             :                     {
    2236           0 :                         pRes->push_back( *it);
    2237             :                     }
    2238             :                 }
    2239           0 :                 break;
    2240             :             default:
    2241             :                 ;   // nothing, prevent compiler warning
    2242             :         }
    2243             :     }
    2244           6 :     ValidateRef( *pRes);    // set #REF! if needed
    2245          12 :     PushTempToken( xRes.get());
    2246             : }
    2247             : 
    2248           1 : void ScInterpreter::ScCurrent()
    2249             : {
    2250           1 :     FormulaTokenRef xTok( PopToken());
    2251           1 :     if (xTok)
    2252             :     {
    2253           1 :         PushTempToken( xTok.get());
    2254           1 :         PushTempToken( xTok.get());
    2255             :     }
    2256             :     else
    2257           0 :         PushError( errUnknownStackVariable);
    2258           1 : }
    2259             : 
    2260           0 : void ScInterpreter::ScStyle()
    2261             : {
    2262           0 :     sal_uInt8 nParamCount = GetByte();
    2263           0 :     if (nParamCount >= 1 && nParamCount <= 3)
    2264             :     {
    2265           0 :         OUString aStyle2;                           // Template after timer
    2266           0 :         if (nParamCount >= 3)
    2267           0 :             aStyle2 = GetString().getString();
    2268           0 :         long nTimeOut = 0;                          // timeout
    2269           0 :         if (nParamCount >= 2)
    2270           0 :             nTimeOut = (long)(GetDouble()*1000.0);
    2271           0 :         OUString aStyle1 = GetString().getString(); // Template for immediate
    2272             : 
    2273           0 :         if (nTimeOut < 0)
    2274           0 :             nTimeOut = 0;
    2275             : 
    2276             :         // Execute request to apply template
    2277           0 :         if ( !pDok->IsClipOrUndo() )
    2278             :         {
    2279           0 :             SfxObjectShell* pShell = pDok->GetDocumentShell();
    2280           0 :             if (pShell)
    2281             :             {
    2282             :                 // notify object shell directly!
    2283             : 
    2284           0 :                 ScRange aRange(aPos);
    2285           0 :                 ScAutoStyleHint aHint( aRange, aStyle1, nTimeOut, aStyle2 );
    2286           0 :                 pShell->Broadcast( aHint );
    2287             :             }
    2288             :         }
    2289             : 
    2290           0 :         PushDouble(0.0);
    2291             :     }
    2292             :     else
    2293           0 :         PushIllegalParameter();
    2294           0 : }
    2295             : 
    2296          12 : static ScDdeLink* lcl_GetDdeLink( sfx2::LinkManager* pLinkMgr,
    2297             :                                 const OUString& rA, const OUString& rT, const OUString& rI, sal_uInt8 nM )
    2298             : {
    2299          12 :     size_t nCount = pLinkMgr->GetLinks().size();
    2300          12 :     for (size_t i=0; i<nCount; i++ )
    2301             :     {
    2302           9 :         ::sfx2::SvBaseLink* pBase = *pLinkMgr->GetLinks()[i];
    2303           9 :         if (pBase->ISA(ScDdeLink))
    2304             :         {
    2305           9 :             ScDdeLink* pLink = static_cast<ScDdeLink*>(pBase);
    2306          27 :             if ( pLink->GetAppl() == rA &&
    2307          18 :                  pLink->GetTopic() == rT &&
    2308          27 :                  pLink->GetItem() == rI &&
    2309           9 :                  pLink->GetMode() == nM )
    2310           9 :                 return pLink;
    2311             :         }
    2312             :     }
    2313             : 
    2314           3 :     return NULL;
    2315             : }
    2316             : 
    2317          12 : void ScInterpreter::ScDde()
    2318             : {
    2319             :     //  application, file, scope
    2320             :     //  application, Topic, Item
    2321             : 
    2322          12 :     sal_uInt8 nParamCount = GetByte();
    2323          12 :     if ( MustHaveParamCount( nParamCount, 3, 4 ) )
    2324             :     {
    2325          12 :         sal_uInt8 nMode = SC_DDE_DEFAULT;
    2326          12 :         if (nParamCount == 4)
    2327           0 :             nMode = (sal_uInt8) ::rtl::math::approxFloor(GetDouble());
    2328          12 :         OUString aItem  = GetString().getString();
    2329          24 :         OUString aTopic = GetString().getString();
    2330          24 :         OUString aAppl  = GetString().getString();
    2331             : 
    2332          12 :         if (nMode > SC_DDE_TEXT)
    2333           0 :             nMode = SC_DDE_DEFAULT;
    2334             : 
    2335             :         //  temporary documents (ScFunctionAccess) have no DocShell
    2336             :         //  and no LinkManager -> abort
    2337             : 
    2338          12 :         sfx2::LinkManager* pLinkMgr = pDok->GetLinkManager();
    2339          12 :         if (!pLinkMgr)
    2340             :         {
    2341           0 :             PushNoValue();
    2342          12 :             return;
    2343             :         }
    2344             : 
    2345             :             // Need to reinterpret after loading (build links)
    2346             : 
    2347          12 :         if ( rArr.IsRecalcModeNormal() )
    2348           0 :             rArr.SetExclusiveRecalcModeOnLoad();
    2349             : 
    2350             :             //  while the link ist not evaluated idle must be disabled (to avoid circular references)
    2351             : 
    2352          12 :         bool bOldEnabled = pDok->IsIdleEnabled();
    2353          12 :         pDok->EnableIdle(false);
    2354             : 
    2355             :             // Get/ Create link object
    2356             : 
    2357          12 :         ScDdeLink* pLink = lcl_GetDdeLink( pLinkMgr, aAppl, aTopic, aItem, nMode );
    2358             : 
    2359             :         //TODO: Save Dde-links (in addition) more efficient at document !!!!!
    2360             :         //      ScDdeLink* pLink = pDok->GetDdeLink( aAppl, aTopic, aItem );
    2361             : 
    2362          12 :         bool bWasError = ( pMyFormulaCell && pMyFormulaCell->GetRawError() != 0 );
    2363             : 
    2364          12 :         if (!pLink)
    2365             :         {
    2366           3 :             pLink = new ScDdeLink( pDok, aAppl, aTopic, aItem, nMode );
    2367           3 :             pLinkMgr->InsertDDELink( pLink, aAppl, aTopic, aItem );
    2368           3 :             if ( pLinkMgr->GetLinks().size() == 1 )                    // erster ?
    2369             :             {
    2370           3 :                 SfxBindings* pBindings = pDok->GetViewBindings();
    2371           3 :                 if (pBindings)
    2372           3 :                     pBindings->Invalidate( SID_LINKS );             // Link-Manager enablen
    2373             :             }
    2374             : 
    2375             :                                     //TODO: evaluate asynchron ???
    2376           3 :             pLink->TryUpdate();     //  TryUpdate doesn't call Update multiple times
    2377             : 
    2378           3 :             if (pMyFormulaCell)
    2379             :             {
    2380             :                 // StartListening after the Update to avoid circular references
    2381           3 :                 pMyFormulaCell->StartListening( *pLink );
    2382             :             }
    2383             :         }
    2384             :         else
    2385             :         {
    2386           9 :             if (pMyFormulaCell)
    2387           9 :                 pMyFormulaCell->StartListening( *pLink );
    2388             :         }
    2389             : 
    2390             :         //  If an new Error from Reschedule appears when the link is executed then reset the errorflag
    2391             : 
    2392             : 
    2393          12 :         if ( pMyFormulaCell && pMyFormulaCell->GetRawError() && !bWasError )
    2394           0 :             pMyFormulaCell->SetErrCode(0);
    2395             : 
    2396             :             //  check the value
    2397             : 
    2398          12 :         const ScMatrix* pLinkMat = pLink->GetResult();
    2399          12 :         if (pLinkMat)
    2400             :         {
    2401             :             SCSIZE nC, nR;
    2402          12 :             pLinkMat->GetDimensions(nC, nR);
    2403          12 :             ScMatrixRef pNewMat = GetNewMat( nC, nR);
    2404          12 :             if (pNewMat)
    2405             :             {
    2406          12 :                 pLinkMat->MatCopy(*pNewMat);        // copy
    2407          12 :                 PushMatrix( pNewMat );
    2408             :             }
    2409             :             else
    2410           0 :                 PushIllegalArgument();
    2411             :         }
    2412             :         else
    2413           0 :             PushNA();
    2414             : 
    2415          12 :         pDok->EnableIdle(bOldEnabled);
    2416          24 :         pLinkMgr->CloseCachedComps();
    2417             :     }
    2418             : }
    2419             : 
    2420           3 : void ScInterpreter::ScBase()
    2421             : {   // Value, Base [, MinLen]
    2422           3 :     sal_uInt8 nParamCount = GetByte();
    2423           3 :     if ( MustHaveParamCount( nParamCount, 2, 3 ) )
    2424             :     {
    2425             :         static const sal_Unicode pDigits[] = {
    2426             :             '0','1','2','3','4','5','6','7','8','9',
    2427             :             'A','B','C','D','E','F','G','H','I','J','K','L','M',
    2428             :             'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    2429             :             0
    2430             :         };
    2431             :         static const int nDigits = (sizeof (pDigits)/sizeof(pDigits[0]))-1;
    2432             :         sal_Int32 nMinLen;
    2433           3 :         if ( nParamCount == 3 )
    2434             :         {
    2435           2 :             double fLen = ::rtl::math::approxFloor( GetDouble() );
    2436           2 :             if ( 1.0 <= fLen && fLen < SAL_MAX_UINT16 )
    2437           2 :                 nMinLen = (sal_Int32) fLen;
    2438           0 :             else if ( fLen == 0.0 )
    2439           0 :                 nMinLen = 1;
    2440             :             else
    2441           0 :                 nMinLen = 0;    // Error
    2442             :         }
    2443             :         else
    2444           1 :             nMinLen = 1;
    2445           3 :         double fBase = ::rtl::math::approxFloor( GetDouble() );
    2446           3 :         double fVal = ::rtl::math::approxFloor( GetDouble() );
    2447           3 :         double fChars = ((fVal > 0.0 && fBase > 0.0) ?
    2448           3 :             (ceil( log( fVal ) / log( fBase ) ) + 2.0) :
    2449           6 :             2.0);
    2450           3 :         if ( fChars >= SAL_MAX_UINT16 )
    2451           0 :             nMinLen = 0;    // Error
    2452             : 
    2453           3 :         if ( !nGlobalError && nMinLen && 2 <= fBase && fBase <= nDigits && 0 <= fVal )
    2454             :         {
    2455           3 :             const sal_Int32 nConstBuf = 128;
    2456             :             sal_Unicode aBuf[nConstBuf];
    2457           3 :             sal_Int32 nBuf = std::max<sal_Int32>( fChars, nMinLen + 1 );
    2458           3 :             sal_Unicode* pBuf = (nBuf <= nConstBuf ? aBuf : new sal_Unicode[nBuf]);
    2459          20 :             for ( sal_Int32 j = 0; j < nBuf; ++j )
    2460             :             {
    2461          17 :                 pBuf[j] = '0';
    2462             :             }
    2463           3 :             sal_Unicode* p = pBuf + nBuf - 1;
    2464           3 :             *p = 0;
    2465           3 :             if ( fVal <= (sal_uLong)(~0) )
    2466             :             {
    2467           3 :                 sal_uLong nVal = (sal_uLong) fVal;
    2468           3 :                 sal_uLong nBase = (sal_uLong) fBase;
    2469          15 :                 while ( nVal && p > pBuf )
    2470             :                 {
    2471           9 :                     *--p = pDigits[ nVal % nBase ];
    2472           9 :                     nVal /= nBase;
    2473             :                 }
    2474           3 :                 fVal = (double) nVal;
    2475             :             }
    2476             :             else
    2477             :             {
    2478           0 :                 bool bDirt = false;
    2479           0 :                 while ( fVal && p > pBuf )
    2480             :                 {
    2481             : //TODO: roundoff error starting with numbers greater than 2**48
    2482             : //                  double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) );
    2483             : // a little bit better:
    2484           0 :                     double fInt = ::rtl::math::approxFloor( fVal / fBase );
    2485           0 :                     double fMult = fInt * fBase;
    2486             : #if OSL_DEBUG_LEVEL > 1
    2487             :                     // =BASIS(1e308;36) => GPF with
    2488             :                     // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult );
    2489             :                     // in spite off previous test if fVal >= fMult
    2490             :                     double fDebug1 = fVal - fMult;
    2491             :                     // fVal    := 7,5975311883090e+290
    2492             :                     // fMult   := 7,5975311883090e+290
    2493             :                     // fDebug1 := 1,3848924157003e+275  <- RoundOff-Error
    2494             :                     // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE
    2495             :                     double fDebug2 = ::rtl::math::approxSub( fVal, fMult );
    2496             :                     // und ::rtl::math::approxSub( fVal, fMult ) == 0
    2497             :                     double fDebug3 = ( fInt ? fVal / fInt : 0.0 );
    2498             : 
    2499             :                     // Actual after strange fDebug1 and fVal < fMult is fDebug2 == fBase, but
    2500             :                     // anyway it can't be compared, then bDirt is executed an everything is good...
    2501             : 
    2502             :                     // prevent compiler warnings
    2503             :                     (void)fDebug1; (void)fDebug2; (void)fDebug3;
    2504             : #endif
    2505             :                     size_t nDig;
    2506           0 :                     if ( fVal < fMult )
    2507             :                     {   // something is wrong there
    2508           0 :                         bDirt = true;
    2509           0 :                         nDig = 0;
    2510             :                     }
    2511             :                     else
    2512             :                     {
    2513           0 :                         double fDig = ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal, fMult ) );
    2514           0 :                         if ( bDirt )
    2515             :                         {
    2516           0 :                             bDirt = false;
    2517           0 :                             --fDig;
    2518             :                         }
    2519           0 :                         if ( fDig <= 0.0 )
    2520           0 :                             nDig = 0;
    2521           0 :                         else if ( fDig >= fBase )
    2522           0 :                             nDig = ((size_t) fBase) - 1;
    2523             :                         else
    2524           0 :                             nDig = (size_t) fDig;
    2525             :                     }
    2526           0 :                     *--p = pDigits[ nDig ];
    2527           0 :                     fVal = fInt;
    2528             :                 }
    2529             :             }
    2530           3 :             if ( fVal )
    2531           0 :                 PushError( errStringOverflow );
    2532             :             else
    2533             :             {
    2534           3 :                 if ( nBuf - (p - pBuf) <= nMinLen )
    2535           2 :                     p = pBuf + nBuf - 1 - nMinLen;
    2536           3 :                 PushStringBuffer( p );
    2537             :             }
    2538           3 :             if ( pBuf != aBuf )
    2539           0 :                 delete [] pBuf;
    2540             :         }
    2541             :         else
    2542           0 :             PushIllegalArgument();
    2543             :     }
    2544           3 : }
    2545             : 
    2546           3 : void ScInterpreter::ScDecimal()
    2547             : {   // Text, Base
    2548           3 :     if ( MustHaveParamCount( GetByte(), 2 ) )
    2549             :     {
    2550           3 :         double fBase = ::rtl::math::approxFloor( GetDouble() );
    2551           3 :         OUString aStr = GetString().getString();
    2552           3 :         if ( !nGlobalError && 2 <= fBase && fBase <= 36 )
    2553             :         {
    2554           3 :             double fVal = 0.0;
    2555           3 :             int nBase = (int) fBase;
    2556           3 :             const sal_Unicode* p = aStr.getStr();
    2557           6 :             while ( *p == ' ' || *p == '\t' )
    2558           0 :                 p++;        // strip leading white space
    2559           3 :             if ( nBase == 16 )
    2560             :             {   // evtl. hex-prefix strippen
    2561           1 :                 if ( *p == 'x' || *p == 'X' )
    2562           0 :                     p++;
    2563           1 :                 else if ( *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X') )
    2564           0 :                     p += 2;
    2565             :             }
    2566          16 :             while ( *p )
    2567             :             {
    2568             :                 int n;
    2569          10 :                 if ( '0' <= *p && *p <= '9' )
    2570           6 :                     n = *p - '0';
    2571           4 :                 else if ( 'A' <= *p && *p <= 'Z' )
    2572           4 :                     n = 10 + (*p - 'A');
    2573           0 :                 else if ( 'a' <= *p && *p <= 'z' )
    2574           0 :                     n = 10 + (*p - 'a');
    2575             :                 else
    2576           0 :                     n = nBase;
    2577          10 :                 if ( nBase <= n )
    2578             :                 {
    2579           0 :                     if ( *(p+1) == 0 &&
    2580           0 :                             ( (nBase ==  2 && (*p == 'b' || *p == 'B'))
    2581           0 :                             ||(nBase == 16 && (*p == 'h' || *p == 'H')) )
    2582             :                         )
    2583             :                         ;       // 101b and F00Dh are ok
    2584             :                     else
    2585             :                     {
    2586           0 :                         PushIllegalArgument();
    2587           3 :                         return ;
    2588             :                     }
    2589             :                 }
    2590             :                 else
    2591          10 :                     fVal = fVal * fBase + n;
    2592          10 :                 p++;
    2593             : 
    2594             :             }
    2595           3 :             PushDouble( fVal );
    2596             :         }
    2597             :         else
    2598           0 :             PushIllegalArgument();
    2599             :     }
    2600             : }
    2601             : 
    2602           6 : void ScInterpreter::ScConvert()
    2603             : {   // Value, FromUnit, ToUnit
    2604           6 :     if ( MustHaveParamCount( GetByte(), 3 ) )
    2605             :     {
    2606           6 :         OUString aToUnit = GetString().getString();
    2607          12 :         OUString aFromUnit = GetString().getString();
    2608           6 :         double fVal = GetDouble();
    2609           6 :         if ( nGlobalError )
    2610           0 :             PushError( nGlobalError);
    2611             :         else
    2612             :         {
    2613             :             // first of all search for the given order; if it can't be found then search for the inverse
    2614             :             double fConv;
    2615           6 :             if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) )
    2616           3 :                 PushDouble( fVal * fConv );
    2617           3 :             else if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) )
    2618           3 :                 PushDouble( fVal / fConv );
    2619             :             else
    2620           0 :                 PushNA();
    2621           6 :         }
    2622             :     }
    2623           6 : }
    2624             : 
    2625           6 : void ScInterpreter::ScRoman()
    2626             : {   // Value [Mode]
    2627           6 :     sal_uInt8 nParamCount = GetByte();
    2628           6 :     if( MustHaveParamCount( nParamCount, 1, 2 ) )
    2629             :     {
    2630           6 :         double fMode = (nParamCount == 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0;
    2631           6 :         double fVal = ::rtl::math::approxFloor( GetDouble() );
    2632           6 :         if( nGlobalError )
    2633           0 :             PushError( nGlobalError);
    2634           6 :         else if( (fMode >= 0.0) && (fMode < 5.0) && (fVal >= 0.0) && (fVal < 4000.0) )
    2635             :         {
    2636             :             static const sal_Unicode pChars[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
    2637             :             static const sal_uInt16 pValues[] = { 1000, 500, 100, 50, 10, 5, 1 };
    2638             :             static const sal_uInt16 nMaxIndex = (sal_uInt16)((sizeof(pValues)/sizeof(pValues[0])) - 1);
    2639             : 
    2640           6 :             OUString aRoman;
    2641           6 :             sal_uInt16 nVal = (sal_uInt16) fVal;
    2642           6 :             sal_uInt16 nMode = (sal_uInt16) fMode;
    2643             : 
    2644          30 :             for( sal_uInt16 i = 0; i <= nMaxIndex / 2; i++ )
    2645             :             {
    2646          24 :                 sal_uInt16 nIndex = 2 * i;
    2647          24 :                 sal_uInt16 nDigit = nVal / pValues[ nIndex ];
    2648             : 
    2649          24 :                 if( (nDigit % 5) == 4 )
    2650             :                 {
    2651             :                     // assert can't happen with nVal<4000 precondition
    2652             :                     assert( ((nDigit == 4) ? (nIndex >= 1) : (nIndex >= 2)));
    2653             : 
    2654          14 :                     sal_uInt16 nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2;
    2655          14 :                     sal_uInt16 nSteps = 0;
    2656          39 :                     while( (nSteps < nMode) && (nIndex < nMaxIndex) )
    2657             :                     {
    2658          11 :                         nSteps++;
    2659          11 :                         if( pValues[ nIndex2 ] - pValues[ nIndex + 1 ] <= nVal )
    2660          11 :                             nIndex++;
    2661             :                         else
    2662           0 :                             nSteps = nMode;
    2663             :                     }
    2664          14 :                     aRoman += OUString( pChars[ nIndex ] );
    2665          14 :                     aRoman += OUString( pChars[ nIndex2 ] );
    2666          14 :                     nVal = sal::static_int_cast<sal_uInt16>( nVal + pValues[ nIndex ] );
    2667          14 :                     nVal = sal::static_int_cast<sal_uInt16>( nVal - pValues[ nIndex2 ] );
    2668             :                 }
    2669             :                 else
    2670             :                 {
    2671          10 :                     if( nDigit > 4 )
    2672             :                     {
    2673             :                         // assert can't happen with nVal<4000 precondition
    2674             :                         assert( nIndex >= 1 );
    2675           0 :                         aRoman += OUString( pChars[ nIndex - 1 ] );
    2676             :                     }
    2677          10 :                     sal_Int32 nPad = nDigit % 5;
    2678          10 :                     if (nPad)
    2679             :                     {
    2680           0 :                         OUStringBuffer aBuf(aRoman);
    2681           0 :                         comphelper::string::padToLength(aBuf, aBuf.getLength() + nPad,
    2682           0 :                             pChars[nIndex]);
    2683           0 :                         aRoman = aBuf.makeStringAndClear();
    2684             :                     }
    2685          10 :                     nVal %= pValues[ nIndex ];
    2686             :                 }
    2687             :             }
    2688             : 
    2689           6 :             PushString( aRoman );
    2690             :         }
    2691             :         else
    2692           0 :             PushIllegalArgument();
    2693             :     }
    2694           6 : }
    2695             : 
    2696          13 : static bool lcl_GetArabicValue( sal_Unicode cChar, sal_uInt16& rnValue, bool& rbIsDec )
    2697             : {
    2698          13 :     switch( cChar )
    2699             :     {
    2700           4 :         case 'M':   rnValue = 1000; rbIsDec = true;     break;
    2701           0 :         case 'D':   rnValue = 500;  rbIsDec = false;    break;
    2702           0 :         case 'C':   rnValue = 100;  rbIsDec = true;     break;
    2703           0 :         case 'L':   rnValue = 50;   rbIsDec = false;    break;
    2704           2 :         case 'X':   rnValue = 10;   rbIsDec = true;     break;
    2705           1 :         case 'V':   rnValue = 5;    rbIsDec = false;    break;
    2706           6 :         case 'I':   rnValue = 1;    rbIsDec = true;     break;
    2707           0 :         default:    return false;
    2708             :     }
    2709          13 :     return true;
    2710             : }
    2711             : 
    2712           2 : void ScInterpreter::ScArabic()
    2713             : {
    2714           2 :     OUString aRoman = GetString().getString();
    2715           2 :     if( nGlobalError )
    2716           0 :         PushError( nGlobalError);
    2717             :     else
    2718             :     {
    2719           2 :         aRoman = aRoman.toAsciiUpperCase();
    2720             : 
    2721           2 :         sal_uInt16 nValue = 0;
    2722           2 :         sal_uInt16 nValidRest = 3999;
    2723           2 :         sal_Int32 nCharIndex = 0;
    2724           2 :         sal_Int32 nCharCount = aRoman.getLength();
    2725           2 :         bool bValid = true;
    2726             : 
    2727          11 :         while( bValid && (nCharIndex < nCharCount) )
    2728             :         {
    2729           7 :             sal_uInt16 nDigit1 = 0;
    2730           7 :             sal_uInt16 nDigit2 = 0;
    2731           7 :             bool bIsDec1 = false;
    2732           7 :             bValid = lcl_GetArabicValue( aRoman[nCharIndex], nDigit1, bIsDec1 );
    2733           7 :             if( bValid && (nCharIndex + 1 < nCharCount) )
    2734             :             {
    2735           6 :                 bool bIsDec2 = false;
    2736           6 :                 bValid = lcl_GetArabicValue( aRoman[nCharIndex + 1], nDigit2, bIsDec2 );
    2737             :             }
    2738           7 :             if( bValid )
    2739             :             {
    2740           7 :                 if( nDigit1 >= nDigit2 )
    2741             :                 {
    2742           6 :                     nValue = sal::static_int_cast<sal_uInt16>( nValue + nDigit1 );
    2743           6 :                     nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2));
    2744           6 :                     bValid = (nValidRest >= nDigit1);
    2745           6 :                     if( bValid )
    2746           6 :                         nValidRest = sal::static_int_cast<sal_uInt16>( nValidRest - nDigit1 );
    2747           6 :                     nCharIndex++;
    2748             :                 }
    2749           1 :                 else if( nDigit1 * 2 != nDigit2 )
    2750             :                 {
    2751           1 :                     sal_uInt16 nDiff = nDigit2 - nDigit1;
    2752           1 :                     nValue = sal::static_int_cast<sal_uInt16>( nValue + nDiff );
    2753           1 :                     bValid = (nValidRest >= nDiff);
    2754           1 :                     if( bValid )
    2755           1 :                         nValidRest = nDigit1 - 1;
    2756           1 :                     nCharIndex += 2;
    2757             :                 }
    2758             :                 else
    2759           0 :                     bValid = false;
    2760             :             }
    2761             :         }
    2762           2 :         if( bValid )
    2763           2 :             PushInt( nValue );
    2764             :         else
    2765           0 :             PushIllegalArgument();
    2766           2 :     }
    2767           2 : }
    2768             : 
    2769           6 : void ScInterpreter::ScHyperLink()
    2770             : {
    2771           6 :     sal_uInt8 nParamCount = GetByte();
    2772           6 :     if ( MustHaveParamCount( nParamCount, 1, 2 ) )
    2773             :     {
    2774           6 :         double fVal = 0.0;
    2775           6 :         svl::SharedString aStr;
    2776           6 :         ScMatValType nResultType = SC_MATVAL_STRING;
    2777             : 
    2778           6 :         if ( nParamCount == 2 )
    2779             :         {
    2780           3 :             switch ( GetStackType() )
    2781             :             {
    2782             :                 case svDouble:
    2783           0 :                     fVal = GetDouble();
    2784           0 :                     nResultType = SC_MATVAL_VALUE;
    2785           0 :                 break;
    2786             :                 case svString:
    2787           3 :                     aStr = GetString();
    2788           3 :                 break;
    2789             :                 case svSingleRef:
    2790             :                 case svDoubleRef:
    2791             :                 {
    2792           0 :                     ScAddress aAdr;
    2793           0 :                     if ( !PopDoubleRefOrSingleRef( aAdr ) )
    2794           0 :                         break;
    2795             : 
    2796           0 :                     ScRefCellValue aCell;
    2797           0 :                     aCell.assign(*pDok, aAdr);
    2798           0 :                     if (aCell.hasEmptyValue())
    2799           0 :                         nResultType = SC_MATVAL_EMPTY;
    2800             :                     else
    2801             :                     {
    2802           0 :                         sal_uInt16 nErr = GetCellErrCode(aCell);
    2803           0 :                         if (nErr)
    2804           0 :                             SetError( nErr);
    2805           0 :                         else if (aCell.hasNumeric())
    2806             :                         {
    2807           0 :                             fVal = GetCellValue(aAdr, aCell);
    2808           0 :                             nResultType = SC_MATVAL_VALUE;
    2809             :                         }
    2810             :                         else
    2811           0 :                             GetCellString(aStr, aCell);
    2812           0 :                     }
    2813             :                 }
    2814           0 :                 break;
    2815             :                 case svMatrix:
    2816           0 :                     nResultType = GetDoubleOrStringFromMatrix( fVal, aStr);
    2817           0 :                 break;
    2818             :                 case svMissing:
    2819             :                 case svEmptyCell:
    2820           0 :                     Pop();
    2821             :                     // mimic xcl
    2822           0 :                     fVal = 0.0;
    2823           0 :                     nResultType = SC_MATVAL_VALUE;
    2824           0 :                 break;
    2825             :                 default:
    2826           0 :                     PopError();
    2827           0 :                     SetError( errIllegalArgument);
    2828             :             }
    2829             :         }
    2830           6 :         svl::SharedString aUrl = GetString();
    2831          12 :         ScMatrixRef pResMat = GetNewMat( 1, 2);
    2832           6 :         if (nGlobalError)
    2833             :         {
    2834           0 :             fVal = CreateDoubleError( nGlobalError);
    2835           0 :             nResultType = SC_MATVAL_VALUE;
    2836             :         }
    2837           6 :         if (nParamCount == 2 || nGlobalError)
    2838             :         {
    2839           6 :             if (ScMatrix::IsValueType( nResultType))
    2840           0 :                 pResMat->PutDouble( fVal, 0);
    2841           3 :             else if (ScMatrix::IsRealStringType( nResultType))
    2842           3 :                 pResMat->PutString(aStr, 0);
    2843             :             else    // EmptyType, EmptyPathType, mimic xcl
    2844           0 :                 pResMat->PutDouble( 0.0, 0 );
    2845             :         }
    2846             :         else
    2847           3 :             pResMat->PutString(aUrl, 0);
    2848           6 :         pResMat->PutString(aUrl, 1);
    2849           6 :         bMatrixFormula = true;
    2850          12 :         PushMatrix(pResMat);
    2851             :     }
    2852           6 : }
    2853             : 
    2854             : /** Resources at the website of the European Commission:
    2855             :     http://ec.europa.eu/economy_finance/euro/adoption/conversion/
    2856             :     http://ec.europa.eu/economy_finance/euro/countries/
    2857             :  */
    2858          12 : static bool lclConvertMoney( const OUString& aSearchUnit, double& rfRate, int& rnDec )
    2859             : {
    2860             :     struct ConvertInfo
    2861             :     {
    2862             :         const sal_Char* pCurrText;
    2863             :         double          fRate;
    2864             :         int             nDec;
    2865             :     };
    2866             :     static const ConvertInfo aConvertTable[] = {
    2867             :         { "EUR", 1.0,      2 },
    2868             :         { "ATS", 13.7603,  2 },
    2869             :         { "BEF", 40.3399,  0 },
    2870             :         { "DEM", 1.95583,  2 },
    2871             :         { "ESP", 166.386,  0 },
    2872             :         { "FIM", 5.94573,  2 },
    2873             :         { "FRF", 6.55957,  2 },
    2874             :         { "IEP", 0.787564, 2 },
    2875             :         { "ITL", 1936.27,  0 },
    2876             :         { "LUF", 40.3399,  0 },
    2877             :         { "NLG", 2.20371,  2 },
    2878             :         { "PTE", 200.482,  2 },
    2879             :         { "GRD", 340.750,  2 },
    2880             :         { "SIT", 239.640,  2 },
    2881             :         { "MTL", 0.429300, 2 },
    2882             :         { "CYP", 0.585274, 2 },
    2883             :         { "SKK", 30.1260,  2 },
    2884             :         { "EEK", 15.6466,  2 },
    2885             :         { "LVL", 0.702804, 2 },
    2886             :         { "LTL", 3.45280,  2 }
    2887             :     };
    2888             : 
    2889             :     static const size_t nConversionCount = sizeof( aConvertTable ) / sizeof( aConvertTable[0] );
    2890          24 :     for ( size_t i = 0; i < nConversionCount; ++i )
    2891          24 :         if ( aSearchUnit.equalsIgnoreAsciiCaseAscii( aConvertTable[i].pCurrText ) )
    2892             :         {
    2893          12 :             rfRate = aConvertTable[i].fRate;
    2894          12 :             rnDec  = aConvertTable[i].nDec;
    2895          12 :             return true;
    2896             :         }
    2897           0 :     return false;
    2898             : }
    2899             : 
    2900           6 : void ScInterpreter::ScEuroConvert()
    2901             : {   //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]]
    2902           6 :     sal_uInt8 nParamCount = GetByte();
    2903           6 :     if ( MustHaveParamCount( nParamCount, 3, 5 ) )
    2904             :     {
    2905           6 :         double nPrecision = 0.0;
    2906           6 :         if ( nParamCount == 5 )
    2907             :         {
    2908           0 :             nPrecision = ::rtl::math::approxFloor(GetDouble());
    2909           0 :             if ( nPrecision < 3 )
    2910             :             {
    2911           0 :                 PushIllegalArgument();
    2912           6 :                 return;
    2913             :             }
    2914             :         }
    2915           6 :         bool bFullPrecision = false;
    2916           6 :         if ( nParamCount >= 4 )
    2917           0 :             bFullPrecision = GetBool();
    2918           6 :         OUString aToUnit = GetString().getString();
    2919          12 :         OUString aFromUnit = GetString().getString();
    2920           6 :         double fVal = GetDouble();
    2921           6 :         if ( nGlobalError )
    2922           0 :             PushError( nGlobalError);
    2923             :         else
    2924             :         {
    2925             :             double fFromRate;
    2926             :             double fToRate;
    2927             :             int    nFromDec;
    2928             :             int    nToDec;
    2929           6 :             OUString aEur( "EUR");
    2930          12 :             if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec )
    2931           6 :                 && lclConvertMoney( aToUnit, fToRate, nToDec ) )
    2932             :             {
    2933             :                 double fRes;
    2934           6 :                 if ( aFromUnit.equalsIgnoreAsciiCase( aToUnit ) )
    2935           0 :                     fRes = fVal;
    2936             :                 else
    2937             :                 {
    2938           6 :                     if ( aFromUnit.equalsIgnoreAsciiCase( aEur ) )
    2939           3 :                        fRes = fVal * fToRate;
    2940             :                     else
    2941             :                     {
    2942           3 :                         double fIntermediate = fVal / fFromRate;
    2943           3 :                         if ( nPrecision )
    2944             :                             fIntermediate = ::rtl::math::round( fIntermediate,
    2945           0 :                                                             (int) nPrecision );
    2946           3 :                         fRes = fIntermediate * fToRate;
    2947             :                     }
    2948           6 :                     if ( !bFullPrecision )
    2949           6 :                         fRes = ::rtl::math::round( fRes, nToDec );
    2950             :                 }
    2951           6 :                 PushDouble( fRes );
    2952             :             }
    2953             :             else
    2954           0 :                 PushIllegalArgument();
    2955           6 :         }
    2956             :     }
    2957             : }
    2958             : 
    2959             : // BAHTTEXT
    2960             : #define UTF8_TH_0       "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
    2961             : #define UTF8_TH_1       "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
    2962             : #define UTF8_TH_2       "\340\270\252\340\270\255\340\270\207"
    2963             : #define UTF8_TH_3       "\340\270\252\340\270\262\340\270\241"
    2964             : #define UTF8_TH_4       "\340\270\252\340\270\265\340\271\210"
    2965             : #define UTF8_TH_5       "\340\270\253\340\271\211\340\270\262"
    2966             : #define UTF8_TH_6       "\340\270\253\340\270\201"
    2967             : #define UTF8_TH_7       "\340\271\200\340\270\210\340\271\207\340\270\224"
    2968             : #define UTF8_TH_8       "\340\271\201\340\270\233\340\270\224"
    2969             : #define UTF8_TH_9       "\340\271\200\340\270\201\340\271\211\340\270\262"
    2970             : #define UTF8_TH_10      "\340\270\252\340\270\264\340\270\232"
    2971             : #define UTF8_TH_11      "\340\271\200\340\270\255\340\271\207\340\270\224"
    2972             : #define UTF8_TH_20      "\340\270\242\340\270\265\340\271\210"
    2973             : #define UTF8_TH_1E2     "\340\270\243\340\271\211\340\270\255\340\270\242"
    2974             : #define UTF8_TH_1E3     "\340\270\236\340\270\261\340\270\231"
    2975             : #define UTF8_TH_1E4     "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
    2976             : #define UTF8_TH_1E5     "\340\271\201\340\270\252\340\270\231"
    2977             : #define UTF8_TH_1E6     "\340\270\245\340\271\211\340\270\262\340\270\231"
    2978             : #define UTF8_TH_DOT0    "\340\270\226\340\271\211\340\270\247\340\270\231"
    2979             : #define UTF8_TH_BAHT    "\340\270\232\340\270\262\340\270\227"
    2980             : #define UTF8_TH_SATANG  "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
    2981             : #define UTF8_TH_MINUS   "\340\270\245\340\270\232"
    2982             : 
    2983             : // local functions
    2984             : namespace {
    2985             : 
    2986           0 : inline void lclSplitBlock( double& rfInt, sal_Int32& rnBlock, double fValue, double fSize )
    2987             : {
    2988           0 :     rnBlock = static_cast< sal_Int32 >( modf( (fValue + 0.1) / fSize, &rfInt ) * fSize + 0.1 );
    2989           0 : }
    2990             : 
    2991             : /** Appends a digit (0 to 9) to the passed string. */
    2992           0 : void lclAppendDigit( OStringBuffer& rText, sal_Int32 nDigit )
    2993             : {
    2994           0 :     switch( nDigit )
    2995             :     {
    2996           0 :         case 0: rText.append( UTF8_TH_0 ); break;
    2997           0 :         case 1: rText.append( UTF8_TH_1 ); break;
    2998           0 :         case 2: rText.append( UTF8_TH_2 ); break;
    2999           0 :         case 3: rText.append( UTF8_TH_3 ); break;
    3000           0 :         case 4: rText.append( UTF8_TH_4 ); break;
    3001           0 :         case 5: rText.append( UTF8_TH_5 ); break;
    3002           0 :         case 6: rText.append( UTF8_TH_6 ); break;
    3003           0 :         case 7: rText.append( UTF8_TH_7 ); break;
    3004           0 :         case 8: rText.append( UTF8_TH_8 ); break;
    3005           0 :         case 9: rText.append( UTF8_TH_9 ); break;
    3006             :         default:    OSL_FAIL( "lclAppendDigit - illegal digit" );
    3007             :     }
    3008           0 : }
    3009             : 
    3010             : /** Appends a value raised to a power of 10: nDigit*10^nPow10.
    3011             :     @param nDigit  A digit in the range from 1 to 9.
    3012             :     @param nPow10  A value in the range from 2 to 5.
    3013             :  */
    3014           0 : void lclAppendPow10( OStringBuffer& rText, sal_Int32 nDigit, sal_Int32 nPow10 )
    3015             : {
    3016             :     OSL_ENSURE( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" );
    3017           0 :     lclAppendDigit( rText, nDigit );
    3018           0 :     switch( nPow10 )
    3019             :     {
    3020           0 :         case 2: rText.append( UTF8_TH_1E2 );   break;
    3021           0 :         case 3: rText.append( UTF8_TH_1E3 );   break;
    3022           0 :         case 4: rText.append( UTF8_TH_1E4 );   break;
    3023           0 :         case 5: rText.append( UTF8_TH_1E5 );   break;
    3024             :         default:    OSL_FAIL( "lclAppendPow10 - illegal power" );
    3025             :     }
    3026           0 : }
    3027             : 
    3028             : /** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
    3029           0 : void lclAppendBlock( OStringBuffer& rText, sal_Int32 nValue )
    3030             : {
    3031             :     OSL_ENSURE( (1 <= nValue) && (nValue <= 999999), "lclAppendBlock - illegal value" );
    3032           0 :     if( nValue >= 100000 )
    3033             :     {
    3034           0 :         lclAppendPow10( rText, nValue / 100000, 5 );
    3035           0 :         nValue %= 100000;
    3036             :     }
    3037           0 :     if( nValue >= 10000 )
    3038             :     {
    3039           0 :         lclAppendPow10( rText, nValue / 10000, 4 );
    3040           0 :         nValue %= 10000;
    3041             :     }
    3042           0 :     if( nValue >= 1000 )
    3043             :     {
    3044           0 :         lclAppendPow10( rText, nValue / 1000, 3 );
    3045           0 :         nValue %= 1000;
    3046             :     }
    3047           0 :     if( nValue >= 100 )
    3048             :     {
    3049           0 :         lclAppendPow10( rText, nValue / 100, 2 );
    3050           0 :         nValue %= 100;
    3051             :     }
    3052           0 :     if( nValue > 0 )
    3053             :     {
    3054           0 :         sal_Int32 nTen = nValue / 10;
    3055           0 :         sal_Int32 nOne = nValue % 10;
    3056           0 :         if( nTen >= 1 )
    3057             :         {
    3058           0 :             if( nTen >= 3 )
    3059           0 :                 lclAppendDigit( rText, nTen );
    3060           0 :             else if( nTen == 2 )
    3061           0 :                 rText.append( UTF8_TH_20 );
    3062           0 :             rText.append( UTF8_TH_10 );
    3063             :         }
    3064           0 :         if( (nTen > 0) && (nOne == 1) )
    3065           0 :             rText.append( UTF8_TH_11 );
    3066           0 :         else if( nOne > 0 )
    3067           0 :             lclAppendDigit( rText, nOne );
    3068             :     }
    3069           0 : }
    3070             : 
    3071             : } // namespace
    3072             : 
    3073           0 : void ScInterpreter::ScBahtText()
    3074             : {
    3075           0 :     sal_uInt8 nParamCount = GetByte();
    3076           0 :     if ( MustHaveParamCount( nParamCount, 1 ) )
    3077             :     {
    3078           0 :         double fValue = GetDouble();
    3079           0 :         if( nGlobalError )
    3080             :         {
    3081           0 :             PushError( nGlobalError);
    3082           0 :             return;
    3083             :         }
    3084             : 
    3085             :         // sign
    3086           0 :         bool bMinus = fValue < 0.0;
    3087           0 :         fValue = fabs( fValue );
    3088             : 
    3089             :         // round to 2 digits after decimal point, fValue contains Satang as integer
    3090           0 :         fValue = ::rtl::math::approxFloor( fValue * 100.0 + 0.5 );
    3091             : 
    3092             :         // split Baht and Satang
    3093           0 :         double fBaht = 0.0;
    3094           0 :         sal_Int32 nSatang = 0;
    3095           0 :         lclSplitBlock( fBaht, nSatang, fValue, 100.0 );
    3096             : 
    3097           0 :         OStringBuffer aText;
    3098             : 
    3099             :         // generate text for Baht value
    3100           0 :         if( fBaht == 0.0 )
    3101             :         {
    3102           0 :             if( nSatang == 0 )
    3103           0 :                 aText.append( UTF8_TH_0 );
    3104             :         }
    3105           0 :         else while( fBaht > 0.0 )
    3106             :         {
    3107           0 :             OStringBuffer aBlock;
    3108           0 :             sal_Int32 nBlock = 0;
    3109           0 :             lclSplitBlock( fBaht, nBlock, fBaht, 1.0e6 );
    3110           0 :             if( nBlock > 0 )
    3111           0 :                 lclAppendBlock( aBlock, nBlock );
    3112             :             // add leading "million", if there will come more blocks
    3113           0 :             if( fBaht > 0.0 )
    3114           0 :                 aBlock.insert( 0, OString(UTF8_TH_1E6 ) );
    3115             : 
    3116           0 :             aText.insert(0, aBlock.makeStringAndClear());
    3117           0 :         }
    3118           0 :         if (!aText.isEmpty())
    3119           0 :             aText.append( UTF8_TH_BAHT );
    3120             : 
    3121             :         // generate text for Satang value
    3122           0 :         if( nSatang == 0 )
    3123             :         {
    3124           0 :             aText.append( UTF8_TH_DOT0 );
    3125             :         }
    3126             :         else
    3127             :         {
    3128           0 :             lclAppendBlock( aText, nSatang );
    3129           0 :             aText.append( UTF8_TH_SATANG );
    3130             :         }
    3131             : 
    3132             :         // add the minus sign
    3133           0 :         if( bMinus )
    3134           0 :             aText.insert( 0, OString( UTF8_TH_MINUS ) );
    3135             : 
    3136           0 :         PushString( OStringToOUString(aText.makeStringAndClear(), RTL_TEXTENCODING_UTF8) );
    3137             :     }
    3138             : }
    3139             : 
    3140          43 : void ScInterpreter::ScGetPivotData()
    3141             : {
    3142          43 :     sal_uInt8 nParamCount = GetByte();
    3143             : 
    3144          43 :     if (!MustHaveParamCount(nParamCount, 2, 30) || (nParamCount % 2) == 1)
    3145             :     {
    3146           0 :         PushError(errNoRef);
    3147           0 :         return;
    3148             :     }
    3149             : 
    3150          43 :     bool bOldSyntax = false;
    3151          43 :     if (nParamCount == 2)
    3152             :     {
    3153             :         // if the first parameter is a ref, assume old syntax
    3154          23 :         StackVar eFirstType = GetStackType(2);
    3155          23 :         if (eFirstType == svSingleRef || eFirstType == svDoubleRef)
    3156          19 :             bOldSyntax = true;
    3157             :     }
    3158             : 
    3159          43 :     std::vector<sheet::DataPilotFieldFilter> aFilters;
    3160          86 :     OUString aDataFieldName;
    3161          43 :     ScRange aBlock;
    3162             : 
    3163          43 :     if (bOldSyntax)
    3164             :     {
    3165          19 :         aDataFieldName = GetString().getString();
    3166             : 
    3167          19 :         switch (GetStackType())
    3168             :         {
    3169             :             case svDoubleRef :
    3170           0 :                 PopDoubleRef(aBlock);
    3171           0 :             break;
    3172             :             case svSingleRef :
    3173             :             {
    3174          19 :                 ScAddress aAddr;
    3175          19 :                 PopSingleRef(aAddr);
    3176          19 :                 aBlock = aAddr;
    3177             :             }
    3178          19 :             break;
    3179             :             default:
    3180           0 :                 PushError(errNoRef);
    3181           0 :                 return;
    3182             :         }
    3183             :     }
    3184             :     else
    3185             :     {
    3186             :         // Standard syntax: separate name/value pairs
    3187             : 
    3188          24 :         sal_uInt16 nFilterCount = nParamCount / 2 - 1;
    3189          24 :         aFilters.resize(nFilterCount);
    3190             : 
    3191          24 :         sal_uInt16 i = nFilterCount;
    3192          70 :         while (i-- > 0)
    3193             :         {
    3194             :             //TODO: should allow numeric constraint values
    3195          22 :             aFilters[i].MatchValue = GetString().getString();
    3196          22 :             aFilters[i].FieldName = GetString().getString();
    3197             :         }
    3198             : 
    3199          24 :         switch (GetStackType())
    3200             :         {
    3201             :             case svDoubleRef :
    3202           0 :                 PopDoubleRef(aBlock);
    3203           0 :             break;
    3204             :             case svSingleRef :
    3205             :             {
    3206          24 :                 ScAddress aAddr;
    3207          24 :                 PopSingleRef(aAddr);
    3208          24 :                 aBlock = aAddr;
    3209             :             }
    3210          24 :             break;
    3211             :             default:
    3212           0 :                 PushError(errNoRef);
    3213           0 :                 return;
    3214             :         }
    3215             : 
    3216          24 :         aDataFieldName = GetString().getString(); // First parameter is data field name.
    3217             :     }
    3218             : 
    3219             :     // NOTE : MS Excel docs claim to use the 'most recent' which is not
    3220             :     // exactly the same as what we do in ScDocument::GetDPAtBlock
    3221             :     // However we do need to use GetDPABlock
    3222          43 :     ScDPObject* pDPObj = pDok->GetDPAtBlock(aBlock);
    3223          43 :     if (!pDPObj)
    3224             :     {
    3225           0 :         PushError(errNoRef);
    3226           0 :         return;
    3227             :     }
    3228             : 
    3229          43 :     if (bOldSyntax)
    3230             :     {
    3231          19 :         OUString aFilterStr = aDataFieldName;
    3232          38 :         std::vector<sheet::GeneralFunction> aFilterFuncs;
    3233          19 :         if (!pDPObj->ParseFilters(aDataFieldName, aFilters, aFilterFuncs, aFilterStr))
    3234             :         {
    3235           0 :             PushError(errNoRef);
    3236           0 :             return;
    3237          19 :         }
    3238             : 
    3239             :         // TODO : For now, we ignore filter functions since we couldn't find a
    3240             :         // live example of how they are supposed to be used. We'll support
    3241             :         // this again once we come across a real-world example.
    3242             :     }
    3243             : 
    3244          43 :     double fVal = pDPObj->GetPivotData(aDataFieldName, aFilters);
    3245          43 :     if (rtl::math::isNan(fVal))
    3246             :     {
    3247           0 :         PushError(errNoRef);
    3248           0 :         return;
    3249             :     }
    3250          86 :     PushDouble(fVal);
    3251         156 : }
    3252             : 
    3253             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11