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 <com/sun/star/util/Date.hpp>
21 #include <com/sun/star/util/XNumberFormatTypes.hpp>
22 #include <com/sun/star/util/NumberFormatter.hpp>
23 
24 #include <string.h>
25 #include <stdio.h>
26 #include <o3tl/any.hxx>
27 #include <rtl/math.hxx>
28 #include <algorithm>
29 #include <cmath>
30 #include <memory>
31 
32 #include "analysisdefs.hxx"
33 #include "analysishelper.hxx"
34 #include <analysis.hrc>
35 #include <strings.hrc>
36 #include "deffuncname.hxx"
37 
38 using namespace                 ::com::sun::star;
39 using namespace sca::analysis;
40 
41 #define UNIQUE              false   // function name does not exist in Calc
42 #define DOUBLE              true    // function name exists in Calc
43 
44 #define STDPAR              false   // all parameters are described
45 #define INTPAR              true    // first parameter is internal
46 
47 #define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \
48     { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr }
49 
50 #define FUNCDATAS( FUNCNAME, DBL, OPT, NUMOFPAR, CAT, SUFFIX ) \
51     { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, SUFFIX }
52 
53 const FuncDataBase pFuncDatas[] =
54 {
55     //                          UNIQUE or   INTPAR or
56     //         function name     DOUBLE      STDPAR     # of param  category
57     FUNCDATA( Workday,          UNIQUE,     INTPAR,     3,          FDCategory::DateTime ),
58     FUNCDATA( Yearfrac,         UNIQUE,     INTPAR,     3,          FDCategory::DateTime ),
59     FUNCDATA( Edate,            UNIQUE,     INTPAR,     2,          FDCategory::DateTime ),
60     FUNCDATAS( Weeknum,         DOUBLE,     INTPAR,     2,          FDCategory::DateTime, "_EXCEL2003" ),
61     FUNCDATA( Eomonth,          UNIQUE,     INTPAR,     2,          FDCategory::DateTime ),
62     FUNCDATAS( Networkdays,     DOUBLE,     INTPAR,     3,          FDCategory::DateTime, "_EXCEL2003" ),
63     FUNCDATA( Iseven,           DOUBLE,     STDPAR,     1,          FDCategory::Inf ),
64     FUNCDATA( Isodd,            DOUBLE,     STDPAR,     1,          FDCategory::Inf ),
65     FUNCDATA( Multinomial,      UNIQUE,     STDPAR,     1,          FDCategory::Math ),
66     FUNCDATA( Seriessum,        UNIQUE,     STDPAR,     4,          FDCategory::Math ),
67     FUNCDATA( Quotient,         UNIQUE,     STDPAR,     2,          FDCategory::Math ),
68     FUNCDATA( Mround,           UNIQUE,     STDPAR,     2,          FDCategory::Math ),
69     FUNCDATA( Sqrtpi,           UNIQUE,     STDPAR,     1,          FDCategory::Math ),
70     FUNCDATA( Randbetween,      UNIQUE,     STDPAR,     2,          FDCategory::Math ),
71     FUNCDATAS( Gcd,             DOUBLE,     INTPAR,     1,          FDCategory::Math, "_EXCEL2003" ),
72     FUNCDATAS( Lcm,             DOUBLE,     INTPAR,     1,          FDCategory::Math, "_EXCEL2003" ),
73     FUNCDATA( Besseli,          UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
74     FUNCDATA( Besselj,          UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
75     FUNCDATA( Besselk,          UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
76     FUNCDATA( Bessely,          UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
77     FUNCDATA( Bin2Oct,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
78     FUNCDATA( Bin2Dec,          UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
79     FUNCDATA( Bin2Hex,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
80     FUNCDATA( Oct2Bin,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
81     FUNCDATA( Oct2Dec,          UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
82     FUNCDATA( Oct2Hex,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
83     FUNCDATA( Dec2Bin,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
84     FUNCDATA( Dec2Hex,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
85     FUNCDATA( Dec2Oct,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
86     FUNCDATA( Hex2Bin,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
87     FUNCDATA( Hex2Dec,          UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
88     FUNCDATA( Hex2Oct,          UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
89     FUNCDATA( Delta,            UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
90     FUNCDATA( Erf,              UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
91     FUNCDATA( Erfc,             UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
92     FUNCDATA( Gestep,           UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
93     FUNCDATA( Factdouble,       UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
94     FUNCDATA( Imabs,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
95     FUNCDATA( Imaginary,        UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
96     FUNCDATA( Impower,          UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
97     FUNCDATA( Imargument,       UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
98     FUNCDATA( Imcos,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
99     FUNCDATA( Imdiv,            UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
100     FUNCDATA( Imexp,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
101     FUNCDATA( Imconjugate,      UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
102     FUNCDATA( Imln,             UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
103     FUNCDATA( Imlog10,          UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
104     FUNCDATA( Imlog2,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
105     FUNCDATA( Improduct,        UNIQUE,     INTPAR,     2,          FDCategory::Tech ),
106     FUNCDATA( Imreal,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
107     FUNCDATA( Imsin,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
108     FUNCDATA( Imsub,            UNIQUE,     STDPAR,     2,          FDCategory::Tech ),
109     FUNCDATA( Imsqrt,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
110     FUNCDATA( Imsum,            UNIQUE,     INTPAR,     1,          FDCategory::Tech ),
111     FUNCDATA( Imtan,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
112     FUNCDATA( Imsec,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
113     FUNCDATA( Imcsc,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
114     FUNCDATA( Imcot,            UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
115     FUNCDATA( Imsinh,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
116     FUNCDATA( Imcosh,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
117     FUNCDATA( Imsech,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
118     FUNCDATA( Imcsch,           UNIQUE,     STDPAR,     1,          FDCategory::Tech ),
119     FUNCDATA( Complex,          UNIQUE,     STDPAR,     3,          FDCategory::Tech ),
120     FUNCDATA( Convert,          UNIQUE,     STDPAR,     3,          FDCategory::Tech ),
121     FUNCDATA( Amordegrc,        UNIQUE,     INTPAR,     7,          FDCategory::Finance ),
122     FUNCDATA( Amorlinc,         UNIQUE,     INTPAR,     7,          FDCategory::Finance ),
123     FUNCDATA( Accrint,          UNIQUE,     INTPAR,     7,          FDCategory::Finance ),
124     FUNCDATA( Accrintm,         UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
125     FUNCDATA( Received,         UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
126     FUNCDATA( Disc,             UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
127     FUNCDATA( Duration,         UNIQUE,     INTPAR,     6,          FDCategory::Finance ),
128     FUNCDATA( Effect,           DOUBLE,     STDPAR,     2,          FDCategory::Finance ),
129     FUNCDATA( Cumprinc,         DOUBLE,     STDPAR,     6,          FDCategory::Finance ),
130     FUNCDATA( Cumipmt,          DOUBLE,     STDPAR,     6,          FDCategory::Finance ),
131     FUNCDATA( Price,            UNIQUE,     INTPAR,     7,          FDCategory::Finance ),
132     FUNCDATA( Pricedisc,        UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
133     FUNCDATA( Pricemat,         UNIQUE,     INTPAR,     6,          FDCategory::Finance ),
134     FUNCDATA( Mduration,        UNIQUE,     INTPAR,     6,          FDCategory::Finance ),
135     FUNCDATA( Nominal,          DOUBLE,     STDPAR,     2,          FDCategory::Finance ),
136     FUNCDATA( Dollarfr,         UNIQUE,     STDPAR,     2,          FDCategory::Finance ),
137     FUNCDATA( Dollarde,         UNIQUE,     STDPAR,     2,          FDCategory::Finance ),
138     FUNCDATA( Yield,            UNIQUE,     INTPAR,     7,          FDCategory::Finance ),
139     FUNCDATA( Yielddisc,        UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
140     FUNCDATA( Yieldmat,         UNIQUE,     INTPAR,     6,          FDCategory::Finance ),
141     FUNCDATA( Tbilleq,          UNIQUE,     INTPAR,     3,          FDCategory::Finance ),
142     FUNCDATA( Tbillprice,       UNIQUE,     INTPAR,     3,          FDCategory::Finance ),
143     FUNCDATA( Tbillyield,       UNIQUE,     INTPAR,     3,          FDCategory::Finance ),
144     FUNCDATA( Oddfprice,        UNIQUE,     INTPAR,     9,          FDCategory::Finance ),
145     FUNCDATA( Oddfyield,        UNIQUE,     INTPAR,     9,          FDCategory::Finance ),
146     FUNCDATA( Oddlprice,        UNIQUE,     INTPAR,     8,          FDCategory::Finance ),
147     FUNCDATA( Oddlyield,        UNIQUE,     INTPAR,     8,          FDCategory::Finance ),
148     FUNCDATA( Xirr,             UNIQUE,     INTPAR,     3,          FDCategory::Finance ),
149     FUNCDATA( Xnpv,             UNIQUE,     STDPAR,     3,          FDCategory::Finance ),
150     FUNCDATA( Intrate,          UNIQUE,     INTPAR,     5,          FDCategory::Finance ),
151     FUNCDATA( Coupncd,          UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
152     FUNCDATA( Coupdays,         UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
153     FUNCDATA( Coupdaysnc,       UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
154     FUNCDATA( Coupdaybs,        UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
155     FUNCDATA( Couppcd,          UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
156     FUNCDATA( Coupnum,          UNIQUE,     INTPAR,     4,          FDCategory::Finance ),
157     FUNCDATA( Fvschedule,       UNIQUE,     STDPAR,     2,          FDCategory::Finance )
158 };
159 #undef FUNCDATA
160 
161 namespace sca::analysis {
162 
DaysInMonth(sal_uInt16 nMonth,sal_uInt16 nYear)163 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
164 {
165     if( (nMonth == 2) && IsLeapYear( nYear ) )
166         return 29;
167     static const sal_uInt16 aDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
168     return aDaysInMonth[ nMonth ];
169 }
170 
171 
172 /**
173  * Convert a date to a count of days starting from 01/01/0001
174  *
175  * The internal representation of a Date used in this Addin
176  * is the number of days between 01/01/0001 and the date
177  * this function converts a Day , Month, Year representation
178  * to this internal Date value.
179  *
180  */
181 
DateToDays(sal_uInt16 nDay,sal_uInt16 nMonth,sal_uInt16 nYear)182 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
183 {
184     sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365;
185     nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
186 
187     for( sal_uInt16 i = 1; i < nMonth; i++ )
188         nDays += DaysInMonth(i,nYear);
189     nDays += nDay;
190 
191     return nDays;
192 }
193 
194 
195 /**
196  * Convert a count of days starting from 01/01/0001 to a date
197  *
198  * The internal representation of a Date used in this Addin
199  * is the number of days between 01/01/0001 and the date
200  * this function converts this internal Date value
201  * to a Day , Month, Year representation of a Date.
202  *
203  */
204 
DaysToDate(sal_Int32 nDays,sal_uInt16 & rDay,sal_uInt16 & rMonth,sal_uInt16 & rYear)205 void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
206 {
207     if( nDays < 0 )
208         throw lang::IllegalArgumentException();
209 
210     sal_Int32   nTempDays;
211     sal_Int32   i = 0;
212     bool        bCalc;
213 
214     do
215     {
216         nTempDays = nDays;
217         rYear = static_cast<sal_uInt16>((nTempDays / 365) - i);
218         nTempDays -= (static_cast<sal_Int32>(rYear) -1) * 365;
219         nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
220         bCalc = false;
221         if ( nTempDays < 1 )
222         {
223             i++;
224             bCalc = true;
225         }
226         else
227         {
228             if ( nTempDays > 365 )
229             {
230                 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
231                 {
232                     i--;
233                     bCalc = true;
234                 }
235             }
236         }
237     }
238     while ( bCalc );
239 
240     rMonth = 1;
241     while ( nTempDays > DaysInMonth( rMonth, rYear ) )
242     {
243         nTempDays -= DaysInMonth( rMonth, rYear );
244         rMonth++;
245     }
246     rDay = static_cast<sal_uInt16>(nTempDays);
247 }
248 
249 
250 /**
251  * Get the null date used by the spreadsheet document
252  *
253  * The internal representation of a Date used in this Addin
254  * is the number of days between 01/01/0001 and the date
255  * this function returns this internal Date value for the document null date
256  *
257  */
258 
GetNullDate(const uno::Reference<beans::XPropertySet> & xOpt)259 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt )
260 {
261     if( xOpt.is() )
262     {
263         try
264         {
265             uno::Any aAny = xOpt->getPropertyValue( u"NullDate"_ustr );
266             util::Date  aDate;
267             if( aAny >>= aDate )
268                 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
269         }
270         catch( uno::Exception& )
271         {
272         }
273     }
274 
275     // no null date available -> no calculations possible
276     throw uno::RuntimeException();
277 }
278 
279 
GetDiffDate360(sal_uInt16 nDay1,sal_uInt16 nMonth1,sal_uInt16 nYear1,bool bLeapYear1,sal_uInt16 nDay2,sal_uInt16 nMonth2,sal_uInt16 nYear2,bool bUSAMethod)280 sal_Int32 GetDiffDate360(
281                 sal_uInt16 nDay1, sal_uInt16 nMonth1, sal_uInt16 nYear1, bool bLeapYear1,
282                 sal_uInt16 nDay2, sal_uInt16 nMonth2, sal_uInt16 nYear2,
283                 bool bUSAMethod )
284 {
285     if( nDay1 == 31 )
286         nDay1--;
287     else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) )
288             nDay1 = 30;
289 
290     if( nDay2 == 31 )
291     {
292         if( bUSAMethod && nDay1 != 30 )
293         {
294             nDay2 = 1;
295             if( nMonth2 == 12 )
296             {
297                 nYear2++;
298                 nMonth2 = 1;
299             }
300             else
301                 nMonth2++;
302         }
303         else
304             nDay2 = 30;
305     }
306 
307     return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360;
308 }
309 
310 
GetDiffDate360(sal_Int32 nNullDate,sal_Int32 nDate1,sal_Int32 nDate2,bool bUSAMethod)311 sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod )
312 {
313     nDate1 += nNullDate;
314     nDate2 += nNullDate;
315 
316     sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;
317 
318     DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
319     DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
320 
321     return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod );
322 }
323 
324 
GetDaysInYears(sal_uInt16 nYear1,sal_uInt16 nYear2)325 sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 )
326 {
327     sal_uInt16  nLeaps = 0;
328     for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ )
329     {
330         if( IsLeapYear( n ) )
331             nLeaps++;
332     }
333 
334     sal_uInt32  nSum = 1;
335     nSum += nYear2;
336     nSum -= nYear1;
337     nSum *= 365;
338     nSum += nLeaps;
339 
340     return nSum;
341 }
342 
343 
GetDiffDate(sal_Int32 nNullDate,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode,sal_Int32 * pOptDaysIn1stYear)344 sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode,
345     sal_Int32* pOptDaysIn1stYear )
346 {
347     bool    bNeg = nStartDate > nEndDate;
348 
349     if( bNeg )
350         std::swap( nStartDate, nEndDate );
351 
352     sal_Int32       nRet;
353 
354     switch( nMode )
355     {
356         case 0:         // 0=USA (NASD) 30/360
357         case 4:         // 4=Europe 30/360
358             {
359             sal_uInt16      nD1, nM1, nY1, nD2, nM2, nY2;
360 
361             nStartDate += nNullDate;
362             nEndDate += nNullDate;
363 
364             DaysToDate( nStartDate, nD1, nM1, nY1 );
365             DaysToDate( nEndDate, nD2, nM2, nY2 );
366 
367             bool            bLeap = IsLeapYear( nY1 );
368             sal_Int32       nDays, nMonths;
369 
370             nMonths = nM2 - nM1;
371             nDays = nD2 - nD1;
372 
373             nMonths += ( nY2 - nY1 ) * 12;
374 
375             nRet = nMonths * 30 + nDays;
376             if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )
377                 nRet -= bLeap? 1 : 2;
378 
379             if( pOptDaysIn1stYear )
380                 *pOptDaysIn1stYear = 360;
381             }
382             break;
383         case 1:         // 1=exact/exact
384             if( pOptDaysIn1stYear )
385             {
386                 sal_uInt16      nD, nM, nY;
387 
388                 DaysToDate( nStartDate + nNullDate, nD, nM, nY );
389 
390                 *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;
391             }
392             nRet = nEndDate - nStartDate;
393             break;
394         case 2:         // 2=exact/360
395             nRet = nEndDate - nStartDate;
396             if( pOptDaysIn1stYear )
397                 *pOptDaysIn1stYear = 360;
398             break;
399         case 3:         //3=exact/365
400             nRet = nEndDate - nStartDate;
401             if( pOptDaysIn1stYear )
402                 *pOptDaysIn1stYear = 365;
403             break;
404         default:
405             throw lang::IllegalArgumentException();
406     }
407 
408     return bNeg? -nRet : nRet;
409 }
410 
411 
GetYearDiff(sal_Int32 nNullDate,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)412 double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
413 {
414     sal_Int32   nDays1stYear;
415     sal_Int32   nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear );
416 
417     return double( nTotalDays ) / double( nDays1stYear );
418 }
419 
420 
GetDaysInYear(sal_Int32 nNullDate,sal_Int32 nDate,sal_Int32 nMode)421 sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode )
422 {
423     switch( nMode )
424     {
425         case 0:         // 0=USA (NASD) 30/360
426         case 2:         // 2=exact/360
427         case 4:         // 4=Europe 30/360
428             return 360;
429         case 1:         // 1=exact/exact
430             {
431             sal_uInt16  nD, nM, nY;
432             nDate += nNullDate;
433             DaysToDate( nDate, nD, nM, nY );
434             return IsLeapYear( nY )? 366 : 365;
435             }
436         case 3:         //3=exact/365
437             return 365;
438         default:
439             throw lang::IllegalArgumentException();
440     }
441 }
442 
443 
444 // tdf69569 making code compliant with change request for ODFF1.2 par 4.11.7.7
445 /**
446  * Function GetYearFrac implements YEARFRAC as defined in:
447  *   Open Document Format for Office Applications version 1.2 Part 2, par. 6.10.24
448  *   The calculations are defined in:
449  *   Open Document Format for Office Applications version 1.2 Part 2, par. 4.11.7
450  */
GetYearFrac(sal_Int32 nNullDate,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)451 double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
452 {
453     if( nStartDate == nEndDate )
454         return 0.0;     // nothing to do...
455 
456     if( nStartDate > nEndDate )
457         std::swap( nStartDate, nEndDate );
458 
459     sal_Int32 nDate1 = nStartDate + nNullDate;
460     sal_Int32 nDate2 = nEndDate + nNullDate;
461 
462     sal_uInt16  nDay1, nDay2;
463     sal_uInt16  nMonth1, nMonth2;
464     sal_uInt16  nYear1, nYear2;
465 
466     DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
467     DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
468 
469     // calculate days between nDate1 and nDate2
470     sal_Int32 nDayDiff;
471     switch( nMode )
472     {
473         case 0:         // 0=USA (NASD) 30/360
474             if ( nDay1 == 31 )
475             {
476                 nDay1--;
477             }
478             if ( nDay1 == 30 && nDay2 == 31 )
479             {
480                 nDay2--;
481             }
482             else
483             {
484                 if ( nMonth1 == 2 && nDay1 == ( IsLeapYear( nYear1 ) ? 29 : 28 ) )
485                 {
486                     nDay1 = 30;
487                     if ( nMonth2 == 2 && nDay2 == ( IsLeapYear( nYear2 ) ? 29 : 28 ) )
488                     {
489                         nDay2 = 30;
490                     }
491                 }
492             }
493             nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
494             break;
495         case 1:         // 1=exact/exact
496         case 2:         // 2=exact/360
497         case 3:         // 3=exact/365
498             nDayDiff = nDate2 - nDate1;
499             break;
500         case 4:         // 4=Europe 30/360
501             if ( nDay1 == 31 )
502             {
503                 nDay1--;
504             }
505             if ( nDay2 == 31 )
506             {
507                 nDay2--;
508             }
509             nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
510             break;
511         default:
512             throw lang::IllegalArgumentException();
513     }
514 
515     //calculate days in year
516     double nDaysInYear;
517     switch( nMode )
518     {
519         case 0:         // 0=USA (NASD) 30/360
520         case 2:         // 2=exact/360
521         case 4:         // 4=Europe 30/360
522             nDaysInYear = 360;
523             break;
524         case 1:         // 1=exact/exact
525             {
526                 const bool isYearDifferent = ( nYear1 != nYear2 );
527                 // ODFv1.2 part 2 section 4.11.7.7.7
528                 if ( isYearDifferent &&
529                      ( ( nYear2 != nYear1 + 1 ) ||
530                        ( nMonth1 < nMonth2 ) ||
531                        ( nMonth1 == nMonth2 && nDay1 < nDay2 ) ) )
532                 {
533                     // return average of days in year between nDate1 and nDate2, inclusive
534                     sal_Int32 nDayCount = 0;
535                     for ( sal_uInt16 i = nYear1; i <= nYear2; i++ )
536                         nDayCount += ( IsLeapYear( i ) ? 366 : 365 );
537 
538                     nDaysInYear = static_cast<double>(nDayCount) / static_cast<double>( nYear2 - nYear1 + 1 );
539                 }
540                 else
541                 {
542                     // as a consequence, !isYearDifferent or
543                     // nYear2 == nYear + 1 and (nMonth1 > nMonth2 or
544                     // (nMonth1 == nMonth2 and nDay1 >= nDay2))
545                     assert( ( !isYearDifferent ||
546                               ( nYear1 + 1 == nYear2 &&
547                                 ( nMonth1 > nMonth2 ||
548                                   ( nMonth1 == nMonth2 || nDay1 >= nDay2 ) ) ) ) );
549 
550                     // ODFv1.2 part 2 section 4.11.7.7.8 (CHANGE REQUEST PENDING, see tdf6959)
551                     if ( !isYearDifferent && IsLeapYear( nYear1 ) )
552                     {
553                         nDaysInYear = 366;
554                     }
555                     else
556                     {
557                         // ODFv1.2 part 2 section 4.11.7.7.9/10 (CHANGE REQUEST PENDING, see tdf69569)
558                         // we need to determine whether there is a 29 February
559                         // between nDate1 (inclusive) and nDate2 (inclusive)
560                         // the case of nYear1 == nYear2 is adequately tested in previous test
561                         if( isYearDifferent &&
562                             ( ( IsLeapYear( nYear1 ) &&
563                                 ( ( nMonth1 < 2 ) || ( ( nMonth1 == 2 ) && ( nDay1 <= 29 ) ) ) ) ||
564                               ( IsLeapYear( nYear2 ) &&
565                                 ( nMonth2 > 2 || ( ( nMonth2 == 2 ) && ( nDay2 == 29 ) ) ) ) ) )
566                         {
567                             nDaysInYear = 366;
568                         }
569                         else
570                         {
571                             nDaysInYear = 365;
572                         }
573                     }
574                 }
575             }
576             break;
577         case 3:         // 3=exact/365
578             nDaysInYear = 365;
579             break;
580         // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
581         default:
582             throw lang::IllegalArgumentException();
583     }
584 
585     return double( nDayDiff ) / nDaysInYear;
586 }
587 
BinomialCoefficient(double n,double k)588 double BinomialCoefficient( double n, double k )
589 {
590     // This method is a copy of BinomKoeff()
591     // found in sc/source/core/tool/interpr3.cxx
592 
593     double nVal = 0.0;
594     k = ::rtl::math::approxFloor(k);
595     if (n < k)
596         nVal = 0.0;
597     else if (k == 0.0)
598         nVal = 1.0;
599     else
600     {
601         nVal = n/k;
602         n--;
603         k--;
604         while (k > 0.0)
605         {
606             nVal *= n/k;
607             k--;
608             n--;
609         }
610     }
611     return nVal;
612 }
613 
GetGcd(double f1,double f2)614 double GetGcd( double f1, double f2 )
615 {
616     double  f = fmod( f1, f2 );
617     while( f > 0.0 )
618     {
619         f1 = f2;
620         f2 = f;
621         f = fmod( f1, f2 );
622     }
623 
624     return f2;
625 }
626 
627 
ConvertToDec(const OUString & aStr,sal_uInt16 nBase,sal_uInt16 nCharLim)628 double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim )
629 {
630     if ( nBase < 2 || nBase > 36 )
631         throw lang::IllegalArgumentException();
632 
633     sal_uInt32      nStrLen = aStr.getLength();
634     if( nStrLen > nCharLim )
635         throw lang::IllegalArgumentException();
636     else if( !nStrLen )
637         return 0.0;
638 
639     double          fVal = 0.0;
640 
641     const sal_Unicode* p = aStr.getStr();
642 
643     sal_uInt16          nFirstDig = 0;
644     bool                bFirstDig = true;
645     double              fBase = nBase;
646 
647     while ( *p )
648     {
649         sal_uInt16      n;
650 
651         if( '0' <= *p && *p <= '9' )
652             n = *p - '0';
653         else if( 'A' <= *p && *p <= 'Z' )
654             n = 10 + ( *p - 'A' );
655         else if ( 'a' <= *p && *p <= 'z' )
656             n = 10 + ( *p - 'a' );
657         else
658             n = nBase;
659 
660         if( n >= nBase )
661             throw lang::IllegalArgumentException(); // illegal char!
662 
663         if( bFirstDig )
664         {
665             bFirstDig = false;
666             nFirstDig = n;
667         }
668         fVal = fVal * fBase + double( n );
669 
670         p++;
671 
672     }
673 
674     if( nStrLen == nCharLim && !bFirstDig && (nFirstDig >= nBase / 2) )
675     {   // handling negative values
676         fVal = ( pow( double( nBase ), double( nCharLim ) ) - fVal );   // complement
677         fVal *= -1.0;
678     }
679 
680     return fVal;
681 }
682 
683 
GetMaxChar(sal_uInt16 nBase)684 static char GetMaxChar( sal_uInt16 nBase )
685 {
686     const char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
687     return c[ nBase ];
688 }
689 
690 
ConvertFromDec(double fNum,double fMin,double fMax,sal_uInt16 nBase,sal_Int32 nPlaces,sal_Int32 nMaxPlaces,bool bUsePlaces)691 OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase,
692     sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces )
693 {
694     fNum = ::rtl::math::approxFloor( fNum );
695     fMin = ::rtl::math::approxFloor( fMin );
696     fMax = ::rtl::math::approxFloor( fMax );
697 
698     if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) )
699         throw lang::IllegalArgumentException();
700 
701     sal_Int64 nNum = static_cast< sal_Int64 >( fNum );
702     bool      bNeg = nNum < 0;
703     if( bNeg )
704         nNum = sal_Int64( pow( double( nBase ), double( nMaxPlaces ) ) ) + nNum;
705 
706     OUString aRet(OUString::number(nNum, nBase).toAsciiUpperCase());
707 
708 
709     if( bUsePlaces )
710     {
711         sal_Int32 nLen = aRet.getLength();
712         if( !bNeg && nLen > nPlaces )
713         {
714             throw lang::IllegalArgumentException();
715         }
716         else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) )
717         {
718             sal_Int32   nLeft = nPlaces - nLen;
719             std::unique_ptr<char[]> p( new char[ nLeft + 1 ] );
720             memset( p.get(), bNeg ? GetMaxChar( nBase ) : '0', nLeft );
721             p[ nLeft ] = 0x00;
722             aRet = OUString( p.get(), nLeft, RTL_TEXTENCODING_MS_1252 ) + aRet;
723         }
724     }
725 
726     return aRet;
727 }
728 
Erf(double x)729 double Erf( double x )
730 {
731     return std::erf(x);
732 }
733 
Erfc(double x)734 double Erfc( double x )
735 {
736     return std::erfc(x);
737 }
738 
IsNum(sal_Unicode c)739 static bool IsNum( sal_Unicode c )
740 {
741     return c >= '0' && c <= '9';
742 }
743 
744 
IsComma(sal_Unicode c)745 static bool IsComma( sal_Unicode c )
746 {
747     return c == '.' || c == ',';
748 }
749 
750 
IsExpStart(sal_Unicode c)751 static bool IsExpStart( sal_Unicode c )
752 {
753     return c == 'e' || c == 'E';
754 }
755 
756 
IsImagUnit(sal_Unicode c)757 static bool IsImagUnit( sal_Unicode c )
758 {
759     return c == 'i' || c == 'j';
760 }
761 
762 
GetVal(sal_Unicode c)763 static sal_uInt16 GetVal( sal_Unicode c )
764 {
765     return sal_uInt16( c - '0' );
766 }
767 
768 
ParseDouble(const sal_Unicode * & rp,double & rRet)769 bool ParseDouble( const sal_Unicode*& rp, double& rRet )
770 {
771     double              fInt = 0.0;
772     double              fFrac = 0.0;
773     double              fMult = 0.1;    // multiplier to multiply digits with, when adding fractional ones
774     sal_Int32           nExp = 0;
775     sal_Int32           nMaxExp = 307;
776     sal_uInt16          nDigCnt = 18;   // max. number of digits to read in, rest doesn't matter
777 
778     enum State  { S_End = 0, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp };
779 
780     State           eS = S_Sign;
781 
782     bool            bNegNum = false;
783     bool            bNegExp = false;
784 
785     const sal_Unicode*  p = rp;
786     sal_Unicode         c;
787 
788     while( eS )
789     {
790         c = *p;
791         switch( eS )
792         {
793             case S_Sign:
794                 if( IsNum( c ) )
795                 {
796                     fInt = GetVal( c );
797                     nDigCnt--;
798                     eS = S_Int;
799                 }
800                 else if( c == '-' )
801                 {
802                     bNegNum = true;
803                     eS = S_IntStart;
804                 }
805                 else if( c == '+' )
806                     eS = S_IntStart;
807                 else if( IsComma( c ) )
808                     eS = S_Frac;
809                 else
810                     return false;
811                 break;
812             case S_IntStart:
813                 if( IsNum( c ) )
814                 {
815                     fInt = GetVal( c );
816                     nDigCnt--;
817                     eS = S_Int;
818                 }
819                 else if( IsComma( c ) )
820                     eS = S_Frac;
821                 else if( IsImagUnit( c ) )
822                 {
823                     rRet = 0.0;
824                     return true;
825                 }
826                 else
827                     return false;
828                 break;
829             case S_Int:
830                 if( IsNum( c ) )
831                 {
832                     fInt *= 10.0;
833                     fInt += double( GetVal( c ) );
834                     nDigCnt--;
835                     if( !nDigCnt )
836                         eS = S_IgnoreIntDigs;
837                 }
838                 else if( IsComma( c ) )
839                     eS = S_Frac;
840                 else if( IsExpStart( c ) )
841                     eS = S_ExpSign;
842                 else
843                     eS = S_End;
844                 break;
845             case S_IgnoreIntDigs:
846                 if( IsNum( c ) )
847                     nExp++;         // just multiply num with 10... ;-)
848                 else if( IsComma( c ) )
849                     eS = S_Frac;
850                 else if( IsExpStart( c ) )
851                     eS = S_ExpSign;
852                 else
853                     eS = S_End;
854                 break;
855             case S_Frac:
856                 if( IsNum( c ) )
857                 {
858                     fFrac += double( GetVal( c ) ) * fMult;
859                     nDigCnt--;
860                     if( nDigCnt )
861                         fMult *= 0.1;
862                     else
863                         eS = S_IgnoreFracDigs;
864                 }
865                 else if( IsExpStart( c ) )
866                     eS = S_ExpSign;
867                 else
868                     eS = S_End;
869                 break;
870             case S_IgnoreFracDigs:
871                 if( IsExpStart( c ) )
872                     eS = S_ExpSign;
873                 else if( !IsNum( c ) )
874                     eS = S_End;
875                 break;
876             case S_ExpSign:
877                 if( IsNum( c ) )
878                 {
879                     nExp = GetVal( c );
880                     eS = S_Exp;
881                 }
882                 else if( c == '-' )
883                 {
884                     bNegExp = true;
885                     eS = S_Exp;
886                 }
887                 else if( c != '+' )
888                     eS = S_End;
889                 break;
890             case S_Exp:
891                 if( IsNum( c ) )
892                 {
893                     nExp *= 10;
894                     nExp += GetVal( c );
895                     if( nExp > nMaxExp )
896                         return false;
897                 }
898                 else
899                     eS = S_End;
900                 break;
901             // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
902             case S_End:
903                 break;
904         }
905 
906         p++;
907     }
908 
909     p--;        // set pointer back to last
910     rp = p;
911 
912     fInt += fFrac;
913 
914     if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error
915     {
916         sal_Int32   nLog10 = sal_Int32( log10( fInt ) );
917 
918         if( bNegExp )
919             nExp = -nExp;
920 
921         if( nLog10 + nExp > nMaxExp )
922             return false;
923 
924         fInt = ::rtl::math::pow10Exp( fInt, nExp );
925     }
926 
927     if( bNegNum )
928         fInt = -fInt;
929 
930     rRet = fInt;
931 
932     return true;
933 }
934 
935 
GetString(double f,bool bLeadingSign,sal_uInt16 nMaxDig)936 OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig )
937 {
938     const int       nBuff = 256;
939     char        aBuff[ nBuff + 1 ];
940     const char*     pFormStr = bLeadingSign? "%+.*g" : "%.*g";
941     int             nLen = snprintf( aBuff, nBuff, pFormStr, int( nMaxDig ), f );
942     // you never know which underlying implementation you get ...
943     aBuff[nBuff] = 0;
944     if ( nLen < 0 || nLen > nBuff )
945         nLen = strlen( aBuff );
946 
947     OUString          aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 );
948 
949     return aRet;
950 }
951 
952 
GetAmordegrc(sal_Int32 nNullDate,double fCost,sal_Int32 nDate,sal_Int32 nFirstPer,double fRestVal,double fPer,double fRate,sal_Int32 nBase)953 double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
954     double fRestVal, double fPer, double fRate, sal_Int32 nBase )
955 {
956     sal_uInt32  nPer = sal_uInt32( fPer );
957     double      fUsePer = 1.0 / fRate;
958     double      fAmorCoeff;
959 
960     if( fUsePer < 3.0 )
961         fAmorCoeff = 1.0;
962     else if( fUsePer < 5.0 )
963         fAmorCoeff = 1.5;
964     else if( fUsePer <= 6.0 )
965         fAmorCoeff = 2.0;
966     else
967         fAmorCoeff = 2.5;
968 
969     fRate *= fAmorCoeff;
970     double      fNRate = ::rtl::math::round( GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost );
971     fCost -= fNRate;
972     double      fRest = fCost - fRestVal;   // aboriginal cost - residual value - sum of all write-downs
973 
974     for( sal_uInt32 n = 0 ; n < nPer ; n++ )
975     {
976         fNRate = ::rtl::math::round( fRate * fCost );
977         fRest -= fNRate;
978 
979         if( fRest < 0.0 )
980         {
981             switch( nPer - n )
982             {
983                 case 0:
984                 case 1:
985                     return ::rtl::math::round( fCost * 0.5 );
986                 default:
987                     return 0.0;
988             }
989         }
990 
991         fCost -= fNRate;
992     }
993 
994     return fNRate;
995 }
996 
997 
GetAmorlinc(sal_Int32 nNullDate,double fCost,sal_Int32 nDate,sal_Int32 nFirstPer,double fRestVal,double fPer,double fRate,sal_Int32 nBase)998 double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
999     double fRestVal, double fPer, double fRate, sal_Int32 nBase )
1000 {
1001     sal_uInt32  nPer = sal_uInt32( fPer );
1002     double      fOneRate = fCost * fRate;
1003     double      fCostDelta = fCost - fRestVal;
1004     double      f0Rate = GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost;
1005     sal_uInt32  nNumOfFullPeriods = sal_uInt32( ( fCost - fRestVal - f0Rate) / fOneRate );
1006 
1007     double fResult = 0.0;
1008     if( nPer == 0 )
1009         fResult = f0Rate;
1010     else if( nPer <= nNumOfFullPeriods )
1011         fResult = fOneRate;
1012     else if( nPer == nNumOfFullPeriods + 1 )
1013         fResult = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
1014 
1015     if ( fResult > 0.0 )
1016         return fResult;
1017     else
1018         return 0.0;
1019 }
1020 
1021 
GetDuration(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,double fCoup,double fYield,sal_Int32 nFreq,sal_Int32 nBase)1022 double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup,
1023     double fYield, sal_Int32 nFreq, sal_Int32 nBase )
1024 {
1025     double          fYearfrac = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1026     double          fNumOfCoups = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1027     double          fDur = 0.0;
1028     const double    f100 = 100.0;
1029     fCoup *= f100 / double( nFreq );    // fCoup is used as cash flow
1030     fYield /= nFreq;
1031     fYield += 1.0;
1032 
1033     double nDiff = fYearfrac * nFreq - fNumOfCoups;
1034 
1035     double          t;
1036 
1037     for( t = 1.0 ; t < fNumOfCoups ; t++ )
1038         fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff );
1039 
1040     fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1041 
1042     double          p = 0.0;
1043     for( t = 1.0 ; t < fNumOfCoups ; t++ )
1044         p += fCoup / pow( fYield, t + nDiff );
1045 
1046     p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1047 
1048     fDur /= p;
1049     fDur /= double( nFreq );
1050 
1051     return fDur;
1052 }
1053 
1054 
GetYieldmat(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nIssue,double fRate,double fPrice,sal_Int32 nBase)1055 double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue,
1056     double fRate, double fPrice, sal_Int32 nBase )
1057 {
1058     double      fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );
1059     double      fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );
1060     double      fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1061 
1062     double      y = 1.0 + fIssMat * fRate;
1063     y /= fPrice / 100.0 + fIssSet * fRate;
1064     y--;
1065     y /= fSetMat;
1066 
1067     return y;
1068 }
1069 
1070 
GetOddfprice(sal_Int32,sal_Int32,sal_Int32,sal_Int32,sal_Int32,double,double,double,sal_Int32,sal_Int32)1071 double GetOddfprice( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1072     sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fYield*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1073     sal_Int32 /*nBase*/ )
1074 {
1075     // If you change this to not unconditionally throw, the
1076     // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1077     // financial.cxx can be removed.
1078     throw uno::RuntimeException();
1079 }
1080 
1081 
getYield_(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,double fCoup,double fPrice,double fRedemp,sal_Int32 nFreq,sal_Int32 nBase)1082 double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice,
1083                     double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1084 {
1085     double      fRate = fCoup;
1086     double      fPriceN = 0.0;
1087     double      fYield1 = 0.0;
1088     double      fYield2 = 1.0;
1089     double      fPrice1 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield1, fRedemp, nFreq, nBase );
1090     double      fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1091     double      fYieldN = ( fYield2 - fYield1 ) * 0.5;
1092 
1093     for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ )
1094     {
1095         fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase );
1096 
1097         if( rtl::math::approxEqual(fPrice, fPrice1) )
1098             return fYield1;
1099         else if( rtl::math::approxEqual(fPrice, fPrice2) )
1100             return fYield2;
1101         else if( rtl::math::approxEqual(fPrice, fPriceN) )
1102             return fYieldN;
1103         else if( fPrice < fPrice2 )
1104         {
1105             fYield2 *= 2.0;
1106             fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1107 
1108             fYieldN = ( fYield2 - fYield1 ) * 0.5;
1109         }
1110         else
1111         {
1112             if( fPrice < fPriceN )
1113             {
1114                 fYield1 = fYieldN;
1115                 fPrice1 = fPriceN;
1116             }
1117             else
1118             {
1119                 fYield2 = fYieldN;
1120                 fPrice2 = fPriceN;
1121             }
1122 
1123             fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) );
1124         }
1125     }
1126 
1127     if( fabs( fPrice - fPriceN ) > fPrice / 100.0 )
1128         throw lang::IllegalArgumentException();      // result not precise enough
1129 
1130     return fYieldN;
1131 }
1132 
1133 
getPrice_(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,double fRate,double fYield,double fRedemp,sal_Int32 nFreq,sal_Int32 nBase)1134 double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield,
1135                     double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1136 {
1137     double      fFreq = nFreq;
1138 
1139     double      fE = GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase );
1140     double      fDSC_E = GetCoupdaysnc( nNullDate, nSettle, nMat, nFreq, nBase ) / fE;
1141     double      fN = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1142     double      fA = GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1143 
1144     double      fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) );
1145     fRet -= 100.0 * fRate / fFreq * fA / fE;
1146 
1147     double      fT1 = 100.0 * fRate / fFreq;
1148     double      fT2 = 1.0 + fYield / fFreq;
1149 
1150     for( double fK = 0.0 ; fK < fN ; fK++ )
1151         fRet += fT1 / pow( fT2, fK + fDSC_E );
1152 
1153     return fRet;
1154 }
1155 
1156 
GetOddfyield(sal_Int32,sal_Int32,sal_Int32,sal_Int32,sal_Int32,double,double,double,sal_Int32,sal_Int32)1157 double GetOddfyield( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1158     sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fPrice*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1159     sal_Int32 /*nBase*/ )
1160 {
1161     // If you change this to not unconditionally throw, the
1162     // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1163     // financial.cxx can be removed.
1164     throw uno::RuntimeException();
1165 }
1166 
1167 
GetOddlprice(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nLastCoup,double fRate,double fYield,double fRedemp,sal_Int32 nFreq,sal_Int32 nBase)1168 double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1169     double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1170 {
1171     double      fFreq = double( nFreq );
1172     double      fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1173     double      fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1174     double      fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1175 
1176     double      p = fRedemp + fDCi * 100.0 * fRate / fFreq;
1177     p /= fDSCi * fYield / fFreq + 1.0;
1178     p -= fAi * 100.0 * fRate / fFreq;
1179 
1180     return p;
1181 }
1182 
1183 
GetOddlyield(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nLastCoup,double fRate,double fPrice,double fRedemp,sal_Int32 nFreq,sal_Int32 nBase)1184 double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1185     double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1186 {
1187     double      fFreq = double( nFreq );
1188     double      fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1189     double      fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1190     double      fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1191 
1192     double      y = fRedemp + fDCi * 100.0 * fRate / fFreq;
1193     y /= fPrice + fAi * 100.0 * fRate / fFreq;
1194     y--;
1195     y *= fFreq / fDSCi;
1196 
1197     return y;
1198 }
1199 
1200 
GetPmt(double fRate,double fNper,double fPv,double fFv,sal_Int32 nPayType)1201 double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType )
1202 {
1203     double      fPmt;
1204     if( fRate == 0.0 )
1205         fPmt = ( fPv + fFv ) / fNper;
1206     else
1207     {
1208         double  fTerm = pow( 1.0 + fRate, fNper );
1209         if( nPayType > 0 )
1210             fPmt = ( fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fRate );
1211         else
1212             fPmt = fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm );
1213     }
1214 
1215     return -fPmt;
1216 }
1217 
1218 
GetFv(double fRate,double fNper,double fPmt,double fPv,sal_Int32 nPayType)1219 double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType )
1220 {
1221     double      fFv;
1222     if( fRate == 0.0 )
1223         fFv = fPv + fPmt * fNper;
1224     else
1225     {
1226         double  fTerm = pow( 1.0 + fRate, fNper );
1227         if( nPayType > 0 )
1228             fFv = fPv * fTerm + fPmt * ( 1.0 + fRate ) * ( fTerm - 1.0 ) / fRate;
1229         else
1230             fFv = fPv * fTerm + fPmt * ( fTerm - 1.0 ) / fRate;
1231     }
1232 
1233     return -fFv;
1234 }
1235 
1236 // financial functions COUP***
1237 
1238 // COUPPCD: find last coupon date before settlement (can be equal to settlement)
1239 /// @throws css::lang::IllegalArgumentException
lcl_GetCouppcd(ScaDate & rDate,const ScaDate & rSettle,const ScaDate & rMat,sal_Int32 nFreq)1240 static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1241 {
1242     rDate = rMat;
1243     rDate.setYear( rSettle.getYear() );
1244     if( rDate < rSettle )
1245         rDate.addYears( 1 );
1246     while( rDate > rSettle )
1247         rDate.addMonths( -12 / nFreq );
1248 }
1249 
GetCouppcd(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1250 double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1251 {
1252     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1253         throw lang::IllegalArgumentException();
1254 
1255     ScaDate aDate;
1256     lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1257     return aDate.getDate( nNullDate );
1258 }
1259 
1260 // COUPNCD: find first coupon date after settlement (is never equal to settlement)
1261 /// @throws css::lang::IllegalArgumentException
lcl_GetCoupncd(ScaDate & rDate,const ScaDate & rSettle,const ScaDate & rMat,sal_Int32 nFreq)1262 static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1263 {
1264     rDate = rMat;
1265     rDate.setYear( rSettle.getYear() );
1266     if( rDate > rSettle )
1267         rDate.addYears( -1 );
1268     while( rDate <= rSettle )
1269         rDate.addMonths( 12 / nFreq );
1270 }
1271 
GetCoupncd(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1272 double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1273 {
1274     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1275         throw lang::IllegalArgumentException();
1276 
1277     ScaDate aDate;
1278     lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1279     return aDate.getDate( nNullDate );
1280 }
1281 
1282 // COUPDAYBS: get day count: coupon date before settlement <-> settlement
GetCoupdaybs(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1283 double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1284 {
1285     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1286         throw lang::IllegalArgumentException();
1287 
1288     ScaDate aSettle( nNullDate, nSettle, nBase );
1289     ScaDate aDate;
1290     lcl_GetCouppcd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1291     return ScaDate::getDiff( aDate, aSettle );
1292 }
1293 
1294 // COUPDAYSNC: get day count: settlement <-> coupon date after settlement
GetCoupdaysnc(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1295 double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1296 {
1297     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1298         throw lang::IllegalArgumentException();
1299 
1300     if( (nBase != 0) && (nBase != 4) )
1301     {
1302         ScaDate aSettle( nNullDate, nSettle, nBase );
1303         ScaDate aDate;
1304         lcl_GetCoupncd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1305         return ScaDate::getDiff( aSettle, aDate );
1306     }
1307     return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1308 }
1309 
1310 // COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement
GetCoupdays(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1311 double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1312 {
1313     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1314         throw lang::IllegalArgumentException();
1315 
1316     if( nBase == 1 )
1317     {
1318         ScaDate aDate;
1319         lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1320         ScaDate aNextDate( aDate );
1321         aNextDate.addMonths( 12 / nFreq );
1322         return ScaDate::getDiff( aDate, aNextDate );
1323     }
1324     return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq;
1325 }
1326 
1327 // COUPNUM: get count of coupon dates
GetCoupnum(sal_Int32 nNullDate,sal_Int32 nSettle,sal_Int32 nMat,sal_Int32 nFreq,sal_Int32 nBase)1328 double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1329 {
1330     if( nSettle >= nMat || isFreqInvalid(nFreq) )
1331         throw lang::IllegalArgumentException();
1332 
1333     ScaDate aMat( nNullDate, nMat, nBase );
1334     ScaDate aDate;
1335     lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), aMat, nFreq );
1336     sal_uInt16 nMonths = (aMat.getYear() - aDate.getYear()) * 12 + aMat.getMonth() - aDate.getMonth();
1337     return static_cast< double >( nMonths * nFreq / 12 );
1338 }
1339 
FuncData(const FuncDataBase & r)1340 FuncData::FuncData(const FuncDataBase& r) :
1341     aIntName( OUString::createFromAscii( r.pIntName ) ),
1342     pUINameID( r.pUINameID ),
1343     pDescrID( r.pDescrID ),
1344     bDouble( r.bDouble ),
1345     bWithOpt( r.bWithOpt ),
1346     nParam( r.nNumOfParams ),
1347     eCat( r.eCat )
1348 {
1349     if (r.pSuffix)
1350         aSuffix = OUString::createFromAscii(r.pSuffix);
1351 
1352     aCompList.resize(2);
1353     aCompList[0] = OUString(r.pCompListID[0], strlen(r.pCompListID[0]), RTL_TEXTENCODING_UTF8);
1354     aCompList[1] = OUString(r.pCompListID[1], strlen(r.pCompListID[1]), RTL_TEXTENCODING_UTF8);
1355 }
1356 
GetStrIndex(sal_uInt16 nParamNum) const1357 sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const
1358 {
1359     if( !bWithOpt )
1360         nParamNum++;
1361 
1362     if( nParamNum > nParam )
1363         return nParam * 2;
1364     else
1365         return nParamNum * 2;
1366 }
1367 
InitFuncDataList(FuncDataList & rList)1368 void InitFuncDataList(FuncDataList& rList)
1369 {
1370     for(const auto & rFuncData : pFuncDatas)
1371         rList.emplace_back(rFuncData);
1372 }
1373 
SortedIndividualInt32List()1374 SortedIndividualInt32List::SortedIndividualInt32List()
1375 {
1376 }
1377 
1378 
~SortedIndividualInt32List()1379 SortedIndividualInt32List::~SortedIndividualInt32List()
1380 {
1381 }
1382 
1383 
Insert(sal_Int32 nDay)1384 void SortedIndividualInt32List::Insert( sal_Int32 nDay )
1385 {
1386     sal_uInt32 nIndex = Count();
1387     while( nIndex )
1388     {
1389         nIndex--;
1390         sal_Int32 nRef = Get( nIndex );
1391         if( nDay == nRef )
1392             return;
1393         else if( nDay > nRef )
1394         {
1395             maVector.insert( maVector.begin() + nIndex + 1, nDay );
1396             return;
1397         }
1398     }
1399     maVector.insert( maVector.begin(), nDay );
1400 }
1401 
1402 
Insert(sal_Int32 nDay,sal_Int32 nNullDate,bool bInsertOnWeekend)1403 void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1404 {
1405     if( !nDay )
1406         return;
1407 
1408     nDay += nNullDate;
1409     if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) )
1410         Insert( nDay );
1411 }
1412 
1413 
Insert(double fDay,sal_Int32 nNullDate,bool bInsertOnWeekend)1414 void SortedIndividualInt32List::Insert(
1415         double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1416 {
1417     if( (fDay < -2147483648.0) || (fDay > 2147483649.0) )
1418         throw lang::IllegalArgumentException();
1419     Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend );
1420 }
1421 
1422 
Find(sal_Int32 nVal) const1423 bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const
1424 {
1425     sal_uInt32  nE = Count();
1426 
1427     if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) )
1428         return false;
1429 
1430     // linear search
1431 
1432     for( sal_uInt32 n = 0 ; n < nE ; n++ )
1433     {
1434         sal_Int32   nRef = Get( n );
1435 
1436         if( nRef == nVal )
1437             return true;
1438         else if( nRef > nVal )
1439             return false;
1440     }
1441     return false;
1442 }
1443 
1444 
InsertHolidayList(const ScaAnyConverter & rAnyConv,const uno::Any & rHolAny,sal_Int32 nNullDate,bool bInsertOnWeekend)1445 void SortedIndividualInt32List::InsertHolidayList(
1446         const ScaAnyConverter& rAnyConv,
1447         const uno::Any& rHolAny,
1448         sal_Int32 nNullDate,
1449         bool bInsertOnWeekend )
1450 {
1451     double fDay;
1452     if( rAnyConv.getDouble( fDay, rHolAny ) )
1453         Insert( fDay, nNullDate, bInsertOnWeekend );
1454 }
1455 
1456 
InsertHolidayList(ScaAnyConverter & rAnyConv,const uno::Reference<beans::XPropertySet> & xOptions,const uno::Any & rHolAny,sal_Int32 nNullDate)1457 void SortedIndividualInt32List::InsertHolidayList(
1458         ScaAnyConverter& rAnyConv,
1459         const uno::Reference< beans::XPropertySet >& xOptions,
1460         const uno::Any& rHolAny,
1461         sal_Int32 nNullDate )
1462 {
1463     rAnyConv.init( xOptions );
1464     if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE )
1465     {
1466         uno::Sequence< uno::Sequence< uno::Any > > aAnySeq;
1467         if( !(rHolAny >>= aAnySeq) )
1468             throw lang::IllegalArgumentException();
1469 
1470         for (const uno::Sequence<uno::Any>& rSubSeq : aAnySeq)
1471         {
1472             for( const uno::Any& rAny : rSubSeq )
1473                 InsertHolidayList( rAnyConv, rAny, nNullDate, false/*bInsertOnWeekend*/ );
1474         }
1475     }
1476     else
1477         InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ );
1478 }
1479 
1480 
Append(const uno::Sequence<uno::Sequence<double>> & rValueSeq)1481 void ScaDoubleList::Append(
1482         const uno::Sequence< uno::Sequence< double > >& rValueSeq )
1483 {
1484     for( const uno::Sequence< double >& rSubSeq : rValueSeq )
1485     {
1486         for( const double fValue : rSubSeq )
1487             Append( fValue );
1488     }
1489 }
1490 
1491 
Append(const uno::Sequence<uno::Sequence<sal_Int32>> & rValueSeq)1492 void ScaDoubleList::Append(
1493         const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq )
1494 {
1495     for( const uno::Sequence< sal_Int32 >& rSubSeq : rValueSeq )
1496     {
1497         for( const sal_Int32 nValue : rSubSeq )
1498             Append( nValue );
1499     }
1500 }
1501 
Append(const ScaAnyConverter & rAnyConv,const uno::Any & rAny,bool bIgnoreEmpty)1502 void ScaDoubleList::Append(
1503         const ScaAnyConverter& rAnyConv,
1504         const uno::Any& rAny,
1505         bool bIgnoreEmpty )
1506 {
1507     if( auto s = o3tl::tryAccess<
1508             css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) )
1509         Append( rAnyConv, *s, bIgnoreEmpty );
1510     else
1511     {
1512         double fValue;
1513         if( rAnyConv.getDouble( fValue, rAny ) )
1514             Append( fValue );
1515         else if( !bIgnoreEmpty )
1516             Append( 0.0 );
1517     }
1518 }
1519 
1520 
Append(const ScaAnyConverter & rAnyConv,const uno::Sequence<uno::Any> & rAnySeq,bool bIgnoreEmpty)1521 void ScaDoubleList::Append(
1522         const ScaAnyConverter& rAnyConv,
1523         const uno::Sequence< uno::Any >& rAnySeq,
1524         bool bIgnoreEmpty )
1525 {
1526     for( const uno::Any& rAny : rAnySeq )
1527         Append( rAnyConv, rAny, bIgnoreEmpty );
1528 }
1529 
1530 
Append(const ScaAnyConverter & rAnyConv,const uno::Sequence<uno::Sequence<uno::Any>> & rAnySeq,bool bIgnoreEmpty)1531 void ScaDoubleList::Append(
1532         const ScaAnyConverter& rAnyConv,
1533         const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq,
1534         bool bIgnoreEmpty )
1535 {
1536     for( const uno::Sequence< uno::Any >& rArray : rAnySeq )
1537         Append( rAnyConv, rArray, bIgnoreEmpty );
1538 }
1539 
Append(ScaAnyConverter & rAnyConv,const uno::Reference<beans::XPropertySet> & xOpt,const uno::Sequence<uno::Any> & rAnySeq)1540 void ScaDoubleList::Append(
1541         ScaAnyConverter& rAnyConv,
1542         const uno::Reference< beans::XPropertySet >& xOpt,
1543         const uno::Sequence< uno::Any >& rAnySeq )
1544 {
1545     rAnyConv.init( xOpt );
1546     Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ );
1547 }
1548 
1549 
CheckInsert(double) const1550 bool ScaDoubleList::CheckInsert( double ) const
1551 {
1552     return true;
1553 }
1554 
1555 
CheckInsert(double fValue) const1556 bool ScaDoubleListGT0::CheckInsert( double fValue ) const
1557 {
1558     if( fValue < 0.0 )
1559         throw lang::IllegalArgumentException();
1560     return fValue > 0.0;
1561 }
1562 
1563 
CheckInsert(double fValue) const1564 bool ScaDoubleListGE0::CheckInsert( double fValue ) const
1565 {
1566     if( fValue < 0.0 )
1567         throw lang::IllegalArgumentException();
1568     return true;
1569 }
1570 
1571 
Complex(const OUString & rStr)1572 Complex::Complex( const OUString& rStr )
1573 {
1574     if( !ParseString( rStr, *this ) )
1575         throw lang::IllegalArgumentException();
1576 }
1577 
1578 
IsImagUnit(sal_Unicode c)1579 inline bool Complex::IsImagUnit( sal_Unicode c )
1580 {
1581     return c == 'i' || c == 'j';
1582 }
1583 
ParseString(const OUString & rStr,Complex & rCompl)1584 bool Complex::ParseString( const OUString& rStr, Complex& rCompl )
1585 {
1586     rCompl.c = '\0';    // do not force a symbol, if only real part present
1587 
1588     const sal_Unicode* pStr = rStr.getStr();
1589 
1590     if( IsImagUnit( *pStr ) && rStr.getLength() == 1)
1591     {
1592         rCompl.r = 0.0;
1593         rCompl.i = 1.0;
1594         rCompl.c = *pStr;
1595         return true;
1596     }
1597 
1598     double                  f;
1599 
1600     if( !ParseDouble( pStr, f ) )
1601         return false;
1602 
1603     switch( *pStr )
1604     {
1605         case '-':   // imag part follows
1606         case '+':
1607             {
1608             double      r = f;
1609             if( IsImagUnit( pStr[ 1 ] ) )
1610             {
1611                 rCompl.c = pStr[ 1 ];
1612                 if( pStr[ 2 ] == 0 )
1613                 {
1614                     rCompl.r = f;
1615                     rCompl.i = ( *pStr == '+' )? 1.0 : -1.0;
1616                     return true;
1617                 }
1618             }
1619             else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) )
1620             {
1621                 rCompl.c = *pStr;
1622                 pStr++;
1623                 if( *pStr == 0 )
1624                 {
1625                     rCompl.r = r;
1626                     rCompl.i = f;
1627                     return true;
1628                 }
1629             }
1630             }
1631             break;
1632         case 'j':
1633         case 'i':
1634             rCompl.c = *pStr;
1635             pStr++;
1636             if( *pStr == 0 )
1637             {
1638                 rCompl.i = f;
1639                 rCompl.r = 0.0;
1640                 return true;
1641             }
1642             break;
1643         case 0:     // only real-part
1644             rCompl.r = f;
1645             rCompl.i = 0.0;
1646             return true;
1647     }
1648 
1649     return false;
1650 }
1651 
1652 
GetString() const1653 OUString Complex::GetString() const
1654 {
1655     finiteOrThrow(r);
1656     finiteOrThrow(i);
1657     OUStringBuffer aRet;
1658 
1659     bool bHasImag = i != 0.0;
1660     bool bHasReal = !bHasImag || (r != 0.0);
1661 
1662     if( bHasReal )
1663         aRet.append(::GetString( r, false ));
1664     if( bHasImag )
1665     {
1666         if( i == 1.0 )
1667         {
1668             if( bHasReal )
1669                 aRet.append('+');
1670         }
1671         else if( i == -1.0 )
1672             aRet.append('-');
1673         else
1674             aRet.append(::GetString( i, bHasReal ));
1675         aRet.append((c != 'j') ? 'i' : 'j');
1676     }
1677 
1678     return aRet.makeStringAndClear();
1679 }
1680 
1681 
Arg() const1682 double Complex::Arg() const
1683 {
1684     if( r == 0.0 && i == 0.0 )
1685         throw lang::IllegalArgumentException();
1686 
1687     double  phi = acos( r / Abs() );
1688 
1689     if( i < 0.0 )
1690         phi = -phi;
1691 
1692     return phi;
1693 }
1694 
1695 
Power(double fPower)1696 void Complex::Power( double fPower )
1697 {
1698     if( r == 0.0 && i == 0.0 )
1699     {
1700         if( fPower <= 0 )
1701             throw lang::IllegalArgumentException();
1702         r = i = 0.0;
1703         return;
1704     }
1705 
1706     double      p, phi;
1707 
1708     p = Abs();
1709 
1710     phi = acos( r / p );
1711     if( i < 0.0 )
1712         phi = -phi;
1713 
1714     p = pow( p, fPower );
1715     phi *= fPower;
1716 
1717     r = cos( phi ) * p;
1718     i = sin( phi ) * p;
1719 }
1720 
1721 
Sqrt()1722 void Complex::Sqrt()
1723 {
1724     static const double fMultConst = M_SQRT1_2;
1725     double  p = Abs();
1726     double  i_ = sqrt( p - r ) * fMultConst;
1727 
1728     r = sqrt( p + r ) * fMultConst;
1729     i = ( i < 0.0 )? -i_ : i_;
1730 }
1731 
1732 
Sin()1733 void Complex::Sin()
1734 {
1735     if( !::rtl::math::isValidArcArg( r ) )
1736         throw lang::IllegalArgumentException();
1737 
1738     if( i )
1739     {
1740         double  r_;
1741 
1742         r_ = sin( r ) * cosh( i );
1743         i = cos( r ) * sinh( i );
1744         r = r_;
1745     }
1746     else
1747         r = sin( r );
1748 }
1749 
1750 
Cos()1751 void Complex::Cos()
1752 {
1753     if( !::rtl::math::isValidArcArg( r ) )
1754         throw lang::IllegalArgumentException();
1755 
1756     if( i )
1757     {
1758         double      r_;
1759 
1760         r_ = cos( r ) * cosh( i );
1761         i = -( sin( r ) * sinh( i ) );
1762         r = r_;
1763     }
1764     else
1765         r = cos( r );
1766 }
1767 
1768 
Div(const Complex & z)1769 void Complex::Div( const Complex& z )
1770 {
1771     if( z.r == 0 && z.i == 0 )
1772         throw lang::IllegalArgumentException();
1773 
1774     double  a1 = r;
1775     double  a2 = z.r;
1776     double  b1 = i;
1777     double  b2 = z.i;
1778 
1779     double  f = 1.0 / ( a2 * a2 + b2 * b2 );
1780 
1781     r = ( a1 * a2 + b1 * b2 ) * f;
1782     i = ( a2 * b1 - a1 * b2 ) * f;
1783 
1784     if( !c ) c = z.c;
1785 }
1786 
1787 
Exp()1788 void Complex::Exp()
1789 {
1790     double  fE = exp( r );
1791     r = fE * cos( i );
1792     i = fE * sin( i );
1793 }
1794 
1795 
Ln()1796 void Complex::Ln()
1797 {
1798     if( r == 0.0 && i == 0.0 )
1799         throw lang::IllegalArgumentException();
1800 
1801     double      fAbs = Abs();
1802     bool        bNegi = i < 0.0;
1803 
1804     i = acos( r / fAbs );
1805 
1806     if( bNegi )
1807         i = -i;
1808 
1809     r = log( fAbs );
1810 }
1811 
1812 
Log10()1813 void Complex::Log10()
1814 {
1815     Ln();
1816     Mult( M_LOG10E );
1817 }
1818 
1819 
Log2()1820 void Complex::Log2()
1821 {
1822     Ln();
1823     Mult( M_LOG2E );
1824 }
1825 
1826 
Tan()1827 void Complex::Tan()
1828 {
1829     if ( i )
1830     {
1831         if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1832             throw lang::IllegalArgumentException();
1833         double fScale =1.0 / ( cos( 2.0 * r ) + cosh( 2.0 * i ));
1834         r = sin( 2.0 * r ) * fScale;
1835         i = sinh( 2.0 * i ) * fScale;
1836     }
1837     else
1838     {
1839         if( !::rtl::math::isValidArcArg( r ) )
1840             throw lang::IllegalArgumentException();
1841         r = tan( r );
1842     }
1843 }
1844 
1845 
Sec()1846 void Complex::Sec()
1847 {
1848     if( i )
1849     {
1850         if( !::rtl::math::isValidArcArg( 2 * r ) )
1851             throw lang::IllegalArgumentException();
1852         double fScale = 1.0 / (cosh( 2.0 * i) + cos ( 2.0 * r));
1853         double  r_;
1854         r_ = 2.0 * cos( r ) * cosh( i ) * fScale;
1855         i = 2.0 * sin( r ) * sinh( i ) * fScale;
1856         r = r_;
1857     }
1858     else
1859     {
1860         if( !::rtl::math::isValidArcArg( r ) )
1861             throw lang::IllegalArgumentException();
1862         r = 1.0 / cos( r );
1863     }
1864 }
1865 
1866 
Csc()1867 void Complex::Csc()
1868 {
1869     if( i )
1870     {
1871         if( !::rtl::math::isValidArcArg( 2 * r ) )
1872             throw lang::IllegalArgumentException();
1873         double fScale = 1.0 / (cosh( 2.0 * i) - cos ( 2.0 * r));
1874         double  r_;
1875         r_ = 2.0 * sin( r ) * cosh( i ) * fScale;
1876         i = -2.0 * cos( r ) * sinh( i ) * fScale;
1877         r = r_;
1878     }
1879     else
1880     {
1881         if( !::rtl::math::isValidArcArg( r ) )
1882             throw lang::IllegalArgumentException();
1883         r = 1.0 / sin( r );
1884     }
1885 }
1886 
1887 
Cot()1888 void Complex::Cot()
1889 {
1890     if ( i )
1891     {
1892         if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1893             throw lang::IllegalArgumentException();
1894         double fScale =1.0 / ( cosh( 2.0 * i ) - cos( 2.0 * r ) );
1895         r = sin( 2.0 * r ) * fScale;
1896         i = - ( sinh( 2.0 * i ) * fScale );
1897     }
1898     else
1899     {
1900         if( !::rtl::math::isValidArcArg( r ) )
1901             throw lang::IllegalArgumentException();
1902         r = 1.0 / tan( r );
1903     }
1904 }
1905 
1906 
Sinh()1907 void Complex::Sinh()
1908 {
1909     if( !::rtl::math::isValidArcArg( r ) )
1910         throw lang::IllegalArgumentException();
1911 
1912     if( i )
1913     {
1914         double  r_;
1915         r_ = sinh( r ) * cos( i );
1916         i = cosh( r ) * sin( i );
1917         r = r_;
1918     }
1919     else
1920         r = sinh( r );
1921 }
1922 
1923 
Cosh()1924 void Complex::Cosh()
1925 {
1926     if( !::rtl::math::isValidArcArg( r ) )
1927         throw lang::IllegalArgumentException();
1928 
1929     if( i )
1930     {
1931         double  r_;
1932         r_ = cosh( r ) * cos( i );
1933         i = sinh( r ) * sin( i );
1934         r = r_;
1935     }
1936     else
1937         r = cosh( r );
1938 }
1939 
1940 
Sech()1941 void Complex::Sech()
1942 {
1943     if ( i )
1944     {
1945         if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1946             throw lang::IllegalArgumentException();
1947         double fScale =1.0 / ( cosh( 2.0 * r ) + cos( 2.0 * i ));
1948         double r_;
1949         r_ = 2.0 * cosh( r ) * cos( i ) * fScale;
1950         i = - (2.0 * sinh( r ) * sin( i ) * fScale );
1951         r = r_ ;
1952     }
1953     else
1954     {
1955         if( !::rtl::math::isValidArcArg( r ) )
1956             throw lang::IllegalArgumentException();
1957         r = 1.0 / cosh( r );
1958     }
1959 }
1960 
1961 
Csch()1962 void Complex::Csch()
1963 {
1964     if ( i )
1965     {
1966         if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1967             throw lang::IllegalArgumentException();
1968         double fScale =1.0 / ( cosh( 2.0 * r ) - cos( 2.0 * i ));
1969         double r_;
1970         r_ = 2.0 * sinh( r ) * cos( i ) * fScale;
1971         i = - ( 2.0 * cosh( r ) * sin( i ) * fScale );
1972         r = r_ ;
1973     }
1974     else
1975     {
1976         if( !::rtl::math::isValidArcArg( r ) )
1977             throw lang::IllegalArgumentException();
1978         r = 1.0 / sinh( r );
1979     }
1980 }
1981 
1982 
~ComplexList()1983 ComplexList::~ComplexList()
1984 {
1985 }
1986 
1987 
Append(const uno::Sequence<uno::Sequence<OUString>> & r)1988 void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r )
1989 {
1990     for( const uno::Sequence< OUString >& rList : r )
1991     {
1992         for( const OUString& rStr : rList )
1993         {
1994             if( !rStr.isEmpty() )
1995                 Append( Complex( rStr ) );
1996         }
1997     }
1998 }
1999 
2000 
Append(const uno::Sequence<uno::Any> & aMultPars)2001 void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars )
2002 {
2003     for( const uno::Any& r : aMultPars )
2004     {
2005         switch( r.getValueTypeClass() )
2006         {
2007             case uno::TypeClass_VOID:       break;
2008             case uno::TypeClass_STRING:
2009                 {
2010                 auto       pStr = o3tl::forceAccess<OUString>(r);
2011 
2012                 if( !pStr->isEmpty() )
2013                     Append( Complex( *pStr ) );
2014                 }
2015                 break;
2016             case uno::TypeClass_DOUBLE:
2017                 Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) );
2018                 break;
2019             case uno::TypeClass_SEQUENCE:
2020                 {
2021                 uno::Sequence< uno::Sequence< uno::Any > >           aValArr;
2022                 if( !(r >>= aValArr) )
2023                     throw lang::IllegalArgumentException();
2024 
2025                 for (const uno::Sequence<uno::Any>& rArr : aValArr)
2026                     Append( rArr );
2027                 }
2028                 break;
2029             default:
2030                 throw lang::IllegalArgumentException();
2031         }
2032     }
2033 }
2034 
ConvertData(const char p[],double fC,ConvertDataClass e,bool bPrefSupport)2035 ConvertData::ConvertData(const char p[], double fC, ConvertDataClass e, bool bPrefSupport)
2036     : fConst(fC)
2037     , aName(p, strlen(p), RTL_TEXTENCODING_MS_1252)
2038     , eClass(e)
2039     , bPrefixSupport(bPrefSupport)
2040 {
2041 }
2042 
~ConvertData()2043 ConvertData::~ConvertData()
2044 {
2045 }
2046 
GetMatchingLevel(const OUString & rRef) const2047 sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const
2048 {
2049     OUString aStr = rRef;
2050     sal_Int32 nLen = rRef.getLength();
2051     sal_Int32 nIndex = rRef.lastIndexOf( '^' );
2052     if( nIndex > 0 && nIndex  == ( nLen - 2 ) )
2053         aStr = aStr.subView( 0, nLen - 2 ) + OUStringChar( aStr[ nLen - 1 ] );
2054     if( aName == aStr )
2055         return 0;
2056     else
2057     {
2058         const sal_Unicode*  p = aStr.getStr();
2059 
2060         nLen = aStr.getLength();
2061         bool bPref = bPrefixSupport;
2062         bool bOneChar = (bPref && nLen > 1 && (aName == p + 1));
2063         if (bOneChar || (bPref && nLen > 2 && (aName == p + 2) &&
2064                     *p == 'd' && *(p+1) == 'a'))
2065         {
2066             sal_Int16       n;
2067             switch( *p )
2068             {
2069                 case 'y':   n = -24;    break;      // yocto
2070                 case 'z':   n = -21;    break;      // zepto
2071                 case 'a':   n = -18;    break;
2072                 case 'f':   n = -15;    break;
2073                 case 'p':   n = -12;    break;
2074                 case 'n':   n = -9;     break;
2075                 case 'u':   n = -6;     break;
2076                 case 'm':   n = -3;     break;
2077                 case 'c':   n = -2;     break;
2078                 case 'd':
2079                     {
2080                         if ( bOneChar )
2081                             n = -1;                 // deci
2082                         else
2083                             n = 1;                  // deca
2084                     }
2085                     break;
2086                 case 'e':   n = 1;      break;
2087                 case 'h':   n = 2;      break;
2088                 case 'k':   n = 3;      break;
2089                 case 'M':   n = 6;      break;
2090                 case 'G':   n = 9;      break;
2091                 case 'T':   n = 12;     break;
2092                 case 'P':   n = 15;     break;
2093                 case 'E':   n = 18;     break;
2094                 case 'Z':   n = 21;     break;      // zetta
2095                 case 'Y':   n = 24;     break;      // yotta
2096                 default:
2097                             n = INV_MATCHLEV;
2098             }
2099 
2100 // We could weed some nonsense out, ODFF doesn't say so though.
2101 #if 0
2102             if (n < 0 && Class() == CDC_Information)
2103                 n = INV_MATCHLEV;   // milli-bits doesn't make sense
2104 #endif
2105 
2106 //! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------
2107             if( n != INV_MATCHLEV )
2108             {
2109                 sal_Unicode cLast = p[ aStr.getLength() - 1 ];
2110                 if( cLast == '2' )
2111                     n *= 2;
2112                 else if( cLast == '3' )
2113                     n *= 3;
2114             }
2115 //! </HACK> -------------------------------------------------------------------
2116 
2117             return n;
2118         }
2119         else if ( nLen > 2 && ( aName == p + 2 ) && ( Class() == CDC_Information ) )
2120         {
2121             const sal_Unicode*  pStr = aStr.getStr();
2122             if ( *(pStr + 1) != 'i')
2123                 return INV_MATCHLEV;
2124             sal_Int16 n;
2125             switch( *pStr )
2126             {
2127                 case 'k':   n = 10;      break;
2128                 case 'M':   n = 20;      break;
2129                 case 'G':   n = 30;      break;
2130                 case 'T':   n = 40;      break;
2131                 case 'P':   n = 50;      break;
2132                 case 'E':   n = 60;      break;
2133                 case 'Z':   n = 70;      break;
2134                 case 'Y':   n = 80;      break;
2135                 default:
2136                             n = INV_MATCHLEV;
2137             }
2138             return n;
2139         }
2140         else
2141             return INV_MATCHLEV;
2142     }
2143 }
2144 
2145 
Convert(double f,const ConvertData & r,sal_Int16 nLevFrom,sal_Int16 nLevTo) const2146 double ConvertData::Convert(
2147     double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2148 {
2149     if( Class() != r.Class() )
2150         throw lang::IllegalArgumentException();
2151 
2152     bool bBinFromLev = ( nLevFrom > 0 && ( nLevFrom % 10 ) == 0 );
2153     bool bBinToLev   = ( nLevTo > 0 && ( nLevTo % 10 ) == 0 );
2154 
2155     if ( Class() == CDC_Information && ( bBinFromLev || bBinToLev ) )
2156     {
2157         if ( bBinFromLev && bBinToLev )
2158         {
2159             nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo );
2160             f *= r.fConst / fConst;
2161             if( nLevFrom )
2162                 f *= pow( 2.0, nLevFrom );
2163         }
2164         else if ( bBinFromLev )
2165             f *= ( r.fConst / fConst ) * ( pow( 2.0, nLevFrom ) / pow( 10.0, nLevTo ) );
2166         else
2167             f *= ( r.fConst / fConst ) * ( pow( 10.0, nLevFrom ) / pow( 2.0, nLevTo ) );
2168         return f;
2169     }
2170 
2171     nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo );    // effective level
2172 
2173     f *= r.fConst / fConst;
2174 
2175     if( nLevFrom )
2176         f = ::rtl::math::pow10Exp( f, nLevFrom );
2177 
2178     return f;
2179 }
2180 
2181 
ConvertFromBase(double f,sal_Int16 n) const2182 double ConvertData::ConvertFromBase( double f, sal_Int16 n ) const
2183 {
2184     return ::rtl::math::pow10Exp( f * fConst, -n );
2185 }
2186 
~ConvertDataLinear()2187 ConvertDataLinear::~ConvertDataLinear()
2188 {
2189 }
2190 
Convert(double f,const ConvertData & r,sal_Int16 nLevFrom,sal_Int16 nLevTo) const2191 double ConvertDataLinear::Convert(
2192     double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2193 {
2194     if( Class() != r.Class() )
2195         throw lang::IllegalArgumentException();
2196     return r.ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo );
2197 }
2198 
2199 
ConvertToBase(double f,sal_Int16 n) const2200 double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const
2201 {
2202     if( n )
2203         f = ::rtl::math::pow10Exp( f, n );
2204 
2205     f /= fConst;
2206     f -= fOffs;
2207 
2208     return f;
2209 }
2210 
2211 
ConvertFromBase(double f,sal_Int16 n) const2212 double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const
2213 {
2214     f += fOffs;
2215     f *= fConst;
2216 
2217     if( n )
2218         f = ::rtl::math::pow10Exp( f, -n );
2219 
2220     return f;
2221 }
2222 
2223 
ConvertDataList()2224 ConvertDataList::ConvertDataList()
2225 {
2226 #define NEWD(str,unit,cl)   maVector.emplace_back(new ConvertData(str,unit,cl))
2227 #define NEWDP(str,unit,cl)  maVector.emplace_back(new ConvertData(str,unit,cl,true))
2228 #define NEWL(str,unit,offs,cl)  maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl))
2229 #define NEWLP(str,unit,offs,cl) maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl,true))
2230 
2231     // *** are extra and not standard Excel Analysis Addin!
2232 
2233     // MASS: 1 Gram is...
2234     NEWDP( "g",         1.0000000000000000E00,  CDC_Mass ); // Gram
2235     NEWD( "sg",         6.8522050005347800E-05, CDC_Mass ); // Pieces
2236     NEWD( "lbm",        2.2046229146913400E-03, CDC_Mass ); // Pound (commercial weight)
2237     NEWDP( "u",         6.0221370000000000E23,  CDC_Mass ); // U (atomic mass)
2238     NEWD( "ozm",        3.5273971800362700E-02, CDC_Mass ); // Ounce (commercial weight)
2239     NEWD( "stone",      1.574730e-04,           CDC_Mass ); // *** Stone
2240     NEWD( "ton",        1.102311e-06,           CDC_Mass ); // *** Ton
2241     NEWD( "grain",      1.543236E01,            CDC_Mass ); // *** Grain
2242     NEWD( "pweight",    7.054792E-01,           CDC_Mass ); // *** Pennyweight
2243     NEWD( "hweight",    1.968413E-05,           CDC_Mass ); // *** Hundredweight
2244     NEWD( "shweight",   2.204623E-05,           CDC_Mass ); // *** Shorthundredweight
2245     NEWD( "brton",      9.842065E-07,           CDC_Mass ); // *** Gross Registered Ton
2246     NEWD( "cwt",        2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight
2247     NEWD( "shweight",   2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight also
2248     NEWD( "uk_cwt",     1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight
2249     NEWD( "lcwt",       1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2250     NEWD( "hweight",    1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2251     NEWD( "uk_ton",     9.8420652761106063E-07, CDC_Mass ); // Imperial ton
2252     NEWD( "LTON",       9.8420652761106063E-07, CDC_Mass ); // Imperial ton also
2253 
2254     // LENGTH: 1 Meter is...
2255     NEWDP( "m",         1.0000000000000000E00,  CDC_Length ); // Meter
2256     NEWD( "mi",         6.2137119223733397E-04, CDC_Length ); // Britsh Mile        6,21371192237333969617434184363e-4
2257     NEWD( "Nmi",        5.3995680345572354E-04, CDC_Length ); // Nautical Mile      5,39956803455723542116630669546e-4
2258     NEWD( "in",         3.9370078740157480E01,  CDC_Length ); // Inch               39,37007874015748031496062992126
2259     NEWD( "ft",         3.2808398950131234E00,  CDC_Length ); // Foot               3,2808398950131233595800524934383
2260     NEWD( "yd",         1.0936132983377078E00,  CDC_Length ); // Yard               1,0936132983377077865266841644794
2261     NEWDP( "ang",       1.0000000000000000E10,  CDC_Length ); // Angstrom
2262     NEWD( "Pica",       2.8346456692913386E03,  CDC_Length ); // Pica Point (1/72 Inch)   2834,6456692913385826771653543307
2263     NEWD( "picapt",     2.8346456692913386E03,  CDC_Length ); // Pica Point (1/72 Inch)   2834,6456692913385826771653543307
2264     NEWD( "pica",       2.36220472441E02,       CDC_Length ); // pica (1/6 Inch)
2265     NEWD( "ell",        8.748906E-01,           CDC_Length ); // *** Ell
2266     NEWDP( "parsec",    3.240779E-17,           CDC_Length ); // *** Parsec
2267     NEWDP( "pc",        3.240779E-17,           CDC_Length ); // *** Parsec also
2268     NEWDP( "lightyear", 1.0570234557732930E-16, CDC_Length ); // *** Light Year
2269     NEWDP( "ly",        1.0570234557732930E-16, CDC_Length ); // *** Light Year also
2270     NEWD( "survey_mi",  6.2136994949494949E-04, CDC_Length ); // U.S. survey mile
2271 
2272     // TIME: 1 Second is...
2273     NEWD( "yr",     3.1688087814028950E-08, CDC_Time ); // Year
2274     NEWD( "day",    1.1574074074074074E-05, CDC_Time ); // Day
2275     NEWD( "d",      1.1574074074074074E-05, CDC_Time ); // Day also
2276     NEWD( "hr",     2.7777777777777778E-04, CDC_Time ); // Hour
2277     NEWD( "mn",     1.6666666666666667E-02, CDC_Time ); // Minute
2278     NEWD( "min",    1.6666666666666667E-02, CDC_Time ); // Minute also
2279     NEWDP( "sec",   1.0000000000000000E00,  CDC_Time ); // Second
2280     NEWDP( "s",     1.0000000000000000E00,  CDC_Time ); // Second also
2281 
2282     // PRESSURE: 1 Pascal is...
2283     NEWDP( "Pa",    1.0000000000000000E00,  CDC_Pressure ); // Pascal
2284     NEWDP( "atm",   9.8692329999819300E-06, CDC_Pressure ); // Atmosphere
2285     NEWDP( "at",    9.8692329999819300E-06, CDC_Pressure ); // Atmosphere also
2286     NEWDP( "mmHg",  7.5006170799862700E-03, CDC_Pressure ); // mm Hg (Mercury)
2287     NEWD( "Torr",   7.5006380000000000E-03, CDC_Pressure ); // *** Torr
2288     NEWD( "psi",    1.4503770000000000E-04, CDC_Pressure ); // *** Psi
2289 
2290     // FORCE: 1 Newton is...
2291     NEWDP( "N",     1.0000000000000000E00,  CDC_Force ); // Newton
2292     NEWDP( "dyn",   1.0000000000000000E05,  CDC_Force ); // Dyn
2293     NEWDP( "dy",    1.0000000000000000E05,  CDC_Force ); // Dyn also
2294     NEWD( "lbf",    2.24808923655339E-01,   CDC_Force ); // Pound-Force
2295     NEWDP( "pond",  1.019716E02,            CDC_Force ); // *** Pond
2296 
2297     // ENERGY: 1 Joule is...
2298     NEWDP( "J",     1.0000000000000000E00,  CDC_Energy ); // Joule
2299     NEWDP( "e",     1.0000000000000000E07,  CDC_Energy ); // Erg  -> https://en.wikipedia.org/wiki/Erg
2300     NEWDP( "c",     2.3900624947346700E-01, CDC_Energy ); // Thermodynamical Calorie
2301     NEWDP( "cal",   2.3884619064201700E-01, CDC_Energy ); // Calorie
2302     NEWDP( "eV",    6.2414570000000000E18,  CDC_Energy ); // Electronvolt
2303     NEWDP( "ev",    6.2414570000000000E18,  CDC_Energy ); // Electronvolt also
2304     NEWD( "HPh",    3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours
2305     NEWD( "hh",     3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours also
2306     NEWDP( "Wh",    2.7777777777777778E-04, CDC_Energy ); // Watt Hours
2307     NEWDP( "wh",    2.7777777777777778E-04, CDC_Energy ); // Watt Hours also
2308     NEWD( "flb",    2.37304222192651E01,    CDC_Energy ); // Foot Pound
2309     NEWD( "BTU",    9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit
2310     NEWD( "btu",    9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit also
2311 
2312     // POWER: 1 Watt is...
2313     NEWDP( "W",     1.0000000000000000E00,  CDC_Power ); // Watt
2314     NEWDP( "w",     1.0000000000000000E00,  CDC_Power ); // Watt also
2315     NEWD( "HP",     1.341022E-03,           CDC_Power ); // Horsepower
2316     NEWD( "h",      1.341022E-03,           CDC_Power ); // Horsepower also
2317     NEWD( "PS",     1.359622E-03,           CDC_Power ); // *** German Pferdestaerke
2318 
2319     // MAGNETISM: 1 Tesla is...
2320     NEWDP( "T",     1.0000000000000000E00,  CDC_Magnetism ); // Tesla
2321     NEWDP( "ga",    1.0000000000000000E04,  CDC_Magnetism ); // Gauss
2322 
2323     // TEMPERATURE: 1 Kelvin is...
2324     NEWL( "C",      1.0000000000000000E00,  -2.7315000000000000E02, CDC_Temperature ); // Celsius
2325     NEWL( "cel",    1.0000000000000000E00,  -2.7315000000000000E02, CDC_Temperature ); // Celsius also
2326     NEWL( "F",      1.8000000000000000E00,  -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit
2327     NEWL( "fah",    1.8000000000000000E00,  -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit also
2328     NEWLP( "K",     1.0000000000000000E00,  +0.0000000000000000E00, CDC_Temperature ); // Kelvin
2329     NEWLP( "kel",   1.0000000000000000E00,  +0.0000000000000000E00, CDC_Temperature ); // Kelvin also
2330     NEWL( "Reau",   8.0000000000000000E-01, -2.7315000000000000E02, CDC_Temperature ); // *** Reaumur
2331     NEWL( "Rank",   1.8000000000000000E00,  +0.0000000000000000E00, CDC_Temperature ); // *** Rankine
2332 
2333     // VOLUME: 1 Liter is...
2334     NEWD( "tsp",        2.0288413621105798E02,  CDC_Volume ); // US teaspoon            1/768 gallon
2335     NEWD( "tbs",        6.7628045403685994E01,  CDC_Volume ); // US tablespoon          1/256 gallon
2336     NEWD( "oz",         3.3814022701842997E01,  CDC_Volume ); // Ounce Liquid           1/128 gallon
2337     NEWD( "cup",        4.2267528377303746E00,  CDC_Volume ); // Cup                    1/16 gallon
2338     NEWD( "pt",         2.1133764188651873E00,  CDC_Volume ); // US Pint                1/8 gallon
2339     NEWD( "us_pt",      2.1133764188651873E00,  CDC_Volume ); // US Pint also
2340     NEWD( "uk_pt",      1.7597539863927023E00,  CDC_Volume ); // UK Pint                1/8 imperial gallon
2341     NEWD( "qt",         1.0566882094325937E00,  CDC_Volume ); // Quart                  1/4 gallon
2342     NEWD( "gal",        2.6417205235814842E-01, CDC_Volume ); // Gallon                 1/3.785411784
2343     NEWDP( "l",         1.0000000000000000E00,  CDC_Volume ); // Liter
2344     NEWDP( "L",         1.0000000000000000E00,  CDC_Volume ); // Liter also
2345     NEWDP( "lt",        1.0000000000000000E00,  CDC_Volume ); // Liter also
2346     NEWDP( "m3",        1.0000000000000000E-03, CDC_Volume ); // *** Cubic Meter
2347     NEWD( "mi3",        2.3991275857892772E-13, CDC_Volume ); // *** Cubic Britsh Mile
2348     NEWD( "Nmi3",       1.5742621468581148E-13, CDC_Volume ); // *** Cubic Nautical Mile
2349     NEWD( "in3",        6.1023744094732284E01,  CDC_Volume ); // *** Cubic Inch
2350     NEWD( "ft3",        3.5314666721488590E-02, CDC_Volume ); // *** Cubic Foot
2351     NEWD( "yd3",        1.3079506193143922E-03, CDC_Volume ); // *** Cubic Yard
2352     NEWDP( "ang3",      1.0000000000000000E27,  CDC_Volume ); // *** Cubic Angstrom
2353     NEWD( "Pica3",      2.2776990435870636E07,  CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2354     NEWD( "picapt3",    2.2776990435870636E07,  CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2355     NEWD( "pica3",      1.31811287245E04,       CDC_Volume ); // *** Cubic Pica (1/6 inch)
2356     NEWD( "barrel",     6.2898107704321051E-03, CDC_Volume ); // *** Barrel (=42gal)
2357     NEWD( "bushel",     2.837759E-02,           CDC_Volume ); // *** Bushel
2358     NEWD( "regton",     3.531467E-04,           CDC_Volume ); // *** Register ton
2359     NEWD( "GRT",        3.531467E-04,           CDC_Volume ); // *** Register ton also
2360     NEWD( "Schooner",   2.3529411764705882E00,  CDC_Volume ); // *** austr. Schooner
2361     NEWD( "Middy",      3.5087719298245614E00,  CDC_Volume ); // *** austr. Middy
2362     NEWD( "Glass",      5.0000000000000000E00,  CDC_Volume ); // *** austr. Glass
2363     NEWD( "Sixpack",    0.5,                    CDC_Volume ); // ***
2364     NEWD( "Humpen",     2.0,                    CDC_Volume ); // ***
2365     NEWD( "ly3",        1.1810108125623799E-51, CDC_Volume ); // *** Cubic light-year
2366     NEWD( "MTON",       1.4125866688595436E00,  CDC_Volume ); // *** Measurement ton
2367     NEWD( "tspm",       2.0000000000000000E02,  CDC_Volume ); // *** Modern teaspoon
2368     NEWD( "uk_gal",     2.1996924829908779E-01,  CDC_Volume ); // U.K. / Imperial gallon        1/4.54609
2369     NEWD( "uk_qt",      8.7987699319635115E-01,  CDC_Volume ); // U.K. / Imperial quart         1/4 imperial gallon
2370 
2371     // 1 Square Meter is...
2372     NEWDP( "m2",        1.0000000000000000E00,  CDC_Area ); // *** Square Meter
2373     NEWD( "mi2",        3.8610215854244585E-07, CDC_Area ); // *** Square Britsh Mile
2374     NEWD( "Nmi2",       2.9155334959812286E-07, CDC_Area ); // *** Square Nautical Mile
2375     NEWD( "in2",        1.5500031000062000E03,  CDC_Area ); // *** Square Inch
2376     NEWD( "ft2",        1.0763910416709722E01,  CDC_Area ); // *** Square Foot
2377     NEWD( "yd2",        1.1959900463010803E00,  CDC_Area ); // *** Square Yard
2378     NEWDP( "ang2",      1.0000000000000000E20,  CDC_Area ); // *** Square Angstrom
2379     NEWD( "Pica2",      8.0352160704321409E06,  CDC_Area ); // *** Square Pica Point (1/72 inch)
2380     NEWD( "picapt2",    8.0352160704321409E06,  CDC_Area ); // *** Square Pica Point (1/72 inch)
2381     NEWD( "pica2",      5.58001116002232E04,    CDC_Area ); // *** Square Pica (1/6 inch)
2382     NEWD( "Morgen",     4.0000000000000000E-04, CDC_Area ); // *** Morgen
2383     NEWDP( "ar",        1.000000E-02,           CDC_Area ); // *** Ar
2384     NEWD( "acre",       2.471053815E-04,        CDC_Area ); // *** Acre
2385     NEWD( "uk_acre",    2.4710538146716534E-04, CDC_Area ); // *** International acre
2386     NEWD( "us_acre",    2.4710439304662790E-04, CDC_Area ); // *** U.S. survey/statute acre
2387     NEWD( "ly2",        1.1172985860549147E-32, CDC_Area ); // *** Square Light-year
2388     NEWD( "ha",         1.000000E-04,           CDC_Area ); // *** Hectare
2389 
2390     // SPEED: 1 Meter per Second is...
2391     NEWDP( "m/s",   1.0000000000000000E00,  CDC_Speed ); // *** Meters per Second
2392     NEWDP( "m/sec", 1.0000000000000000E00,  CDC_Speed ); // *** Meters per Second also
2393     NEWDP( "m/h",   3.6000000000000000E03,  CDC_Speed ); // *** Meters per Hour
2394     NEWDP( "m/hr",  3.6000000000000000E03,  CDC_Speed ); // *** Meters per Hour also
2395     NEWD( "mph",    2.2369362920544023E00,  CDC_Speed ); // *** Britsh Miles per Hour
2396     NEWD( "kn",     1.9438444924406048E00,  CDC_Speed ); // *** Knot = Nautical Miles per Hour
2397     NEWD( "admkn",  1.9438446603753486E00,  CDC_Speed ); // *** Admiralty Knot
2398     NEWD( "ludicrous speed", 2.0494886343432328E-14, CDC_Speed ); // ***
2399     NEWD( "ridiculous speed", 4.0156958471424288E-06, CDC_Speed); // ***
2400 
2401     // INFORMATION: 1 Bit is...
2402     NEWDP( "bit",   1.00E00,  CDC_Information); // *** Bit
2403     NEWDP( "byte",  1.25E-01, CDC_Information); // *** Byte
2404 }
2405 
2406 
~ConvertDataList()2407 ConvertDataList::~ConvertDataList()
2408 {
2409 }
2410 
2411 
Convert(double fVal,const OUString & rFrom,const OUString & rTo)2412 double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo )
2413 {
2414     ConvertData*    pFrom = nullptr;
2415     ConvertData*    pTo = nullptr;
2416     bool            bSearchFrom = true;
2417     bool            bSearchTo = true;
2418     sal_Int16       nLevelFrom = 0;
2419     sal_Int16       nLevelTo = 0;
2420 
2421     for( const auto& rItem : maVector )
2422     {
2423         ConvertData*    p = rItem.get();
2424         if( bSearchFrom )
2425         {
2426             sal_Int16   n = p->GetMatchingLevel( rFrom );
2427             if( n != INV_MATCHLEV )
2428             {
2429                 if( n )
2430                 {   // only first match for partial equality rulz a little bit more
2431                     pFrom = p;
2432                     nLevelFrom = n;
2433                 }
2434                 else
2435                 {   // ... but exact match rulz most
2436                     pFrom = p;
2437                     bSearchFrom = false;
2438                     nLevelFrom = n;
2439                 }
2440             }
2441         }
2442 
2443         if( bSearchTo )
2444         {
2445             sal_Int16   n = p->GetMatchingLevel( rTo );
2446             if( n != INV_MATCHLEV )
2447             {
2448                 if( n )
2449                 {   // only first match for partial equality rulz a little bit more
2450                     pTo = p;
2451                     nLevelTo = n;
2452                 }
2453                 else
2454                 {   // ... but exact match rulz most
2455                     pTo = p;
2456                     bSearchTo = false;
2457                     nLevelTo = n;
2458                 }
2459             }
2460         }
2461 
2462         if( !bSearchFrom && !bSearchTo )
2463             break;
2464     }
2465 
2466     if( !pFrom || !pTo )
2467         throw lang::IllegalArgumentException();
2468 
2469     return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo );
2470 }
2471 
2472 
ScaDate()2473 ScaDate::ScaDate() :
2474     nOrigDay( 1 ),
2475     nDay( 1 ),
2476     nMonth( 1 ),
2477     nYear( 1900 ),
2478     bLastDayMode( true ),
2479     bLastDay( false ),
2480     b30Days( false ),
2481     bUSMode( false )
2482 {
2483 }
2484 
ScaDate(sal_Int32 nNullDate,sal_Int32 nDate,sal_Int32 nBase)2485 ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase )
2486 {
2487     DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear );
2488     bLastDayMode = (nBase != 5);
2489     bLastDay = (nOrigDay >= ::DaysInMonth( nMonth, nYear ));
2490     b30Days = (nBase == 0) || (nBase == 4);
2491     bUSMode = (nBase == 0);
2492     setDay();
2493 }
2494 
ScaDate(const ScaDate & rCopy)2495 ScaDate::ScaDate( const ScaDate& rCopy ) :
2496     nOrigDay( rCopy.nOrigDay ),
2497     nDay( rCopy.nDay ),
2498     nMonth( rCopy.nMonth ),
2499     nYear( rCopy.nYear ),
2500     bLastDayMode( rCopy.bLastDayMode ),
2501     bLastDay( rCopy.bLastDay ),
2502     b30Days( rCopy.b30Days ),
2503     bUSMode( rCopy.bUSMode )
2504 {
2505 }
2506 
operator =(const ScaDate & rCopy)2507 ScaDate& ScaDate::operator=( const ScaDate& rCopy )
2508 {
2509     if( this != &rCopy )
2510     {
2511         nOrigDay = rCopy.nOrigDay;
2512         nDay = rCopy.nDay;
2513         nMonth = rCopy.nMonth;
2514         nYear = rCopy.nYear;
2515         bLastDayMode = rCopy.bLastDayMode;
2516         bLastDay = rCopy.bLastDay;
2517         b30Days = rCopy.b30Days;
2518         bUSMode = rCopy.bUSMode;
2519     }
2520     return *this;
2521 }
2522 
setDay()2523 void ScaDate::setDay()
2524 {
2525     if( b30Days )
2526     {
2527         // 30-days-mode: set nDay to 30 if original was last day in month
2528         nDay = std::min( nOrigDay, static_cast< sal_uInt16 >( 30 ) );
2529         if( bLastDay || (nDay >= ::DaysInMonth( nMonth, nYear )) )
2530             nDay = 30;
2531     }
2532     else
2533     {
2534         // set nDay to last day in this month if original was last day
2535         sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2536         nDay = bLastDay ? nLastDay : std::min( nOrigDay, nLastDay );
2537     }
2538 }
2539 
getDaysInMonthRange(sal_uInt16 nFrom,sal_uInt16 nTo) const2540 sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2541 {
2542     if( nFrom > nTo )
2543         return 0;
2544 
2545     sal_Int32 nRet = 0;
2546     if( b30Days )
2547         nRet = (nTo - nFrom + 1) * 30;
2548     else
2549     {
2550         for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )
2551             nRet += getDaysInMonth( nMonthIx );
2552     }
2553     return nRet;
2554 }
2555 
getDaysInYearRange(sal_uInt16 nFrom,sal_uInt16 nTo) const2556 sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2557 {
2558     if( nFrom > nTo )
2559         return 0;
2560 
2561     return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo );
2562 }
2563 
doAddYears(sal_Int32 nYearCount)2564 void ScaDate::doAddYears( sal_Int32 nYearCount )
2565 {
2566     sal_Int32 nNewYear = nYearCount + nYear;
2567     if( (nNewYear < 0) || (nNewYear > 0x7FFF) )
2568         throw lang::IllegalArgumentException();
2569     nYear = static_cast< sal_uInt16 >( nNewYear );
2570 }
2571 
addMonths(sal_Int32 nMonthCount)2572 void ScaDate::addMonths( sal_Int32 nMonthCount )
2573 {
2574     sal_Int32 nNewMonth = nMonthCount + nMonth;
2575     if( nNewMonth > 12 )
2576     {
2577         --nNewMonth;
2578         doAddYears( nNewMonth / 12 );
2579         nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1;
2580     }
2581     else if( nNewMonth < 1 )
2582     {
2583         doAddYears( nNewMonth / 12 - 1 );
2584         nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 );
2585     }
2586     else
2587         nMonth = static_cast< sal_uInt16 >( nNewMonth );
2588     setDay();
2589 }
2590 
getDate(sal_Int32 nNullDate) const2591 sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const
2592 {
2593     sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2594     sal_uInt16 nRealDay = (bLastDayMode && bLastDay) ? nLastDay : std::min( nLastDay, nOrigDay );
2595     return ::DateToDays( nRealDay, nMonth, nYear ) - nNullDate;
2596 }
2597 
getDiff(const ScaDate & rFrom,const ScaDate & rTo)2598 sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo )
2599 {
2600     if( rFrom > rTo )
2601         return getDiff( rTo, rFrom );
2602 
2603     sal_Int32 nDiff = 0;
2604     ScaDate aFrom( rFrom );
2605     ScaDate aTo( rTo );
2606 
2607     if( rTo.b30Days )
2608     {
2609         // corrections for base 0 (US NASD)
2610         if( rTo.bUSMode )
2611         {
2612             if( ((rFrom.nMonth == 2) || (rFrom.nDay < 30)) && (aTo.nOrigDay == 31) )
2613                 aTo.nDay = 31;
2614             else if( (aTo.nMonth == 2) && aTo.bLastDay )
2615                 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2616         }
2617         // corrections for base 4 (Europe)
2618         else
2619         {
2620             if( (aFrom.nMonth == 2) && (aFrom.nDay == 30) )
2621                 aFrom.nDay = ::DaysInMonth( 2, aFrom.nYear );
2622             if( (aTo.nMonth == 2) && (aTo.nDay == 30) )
2623                 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2624         }
2625     }
2626 
2627     if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) )
2628     {
2629         // move aFrom to 1st day of next month
2630         nDiff = aFrom.getDaysInMonth() - aFrom.nDay + 1;
2631         aFrom.nOrigDay = aFrom.nDay = 1;
2632         aFrom.bLastDay = false;
2633         aFrom.addMonths( 1 );
2634 
2635         if( aFrom.nYear < aTo.nYear )
2636         {
2637             // move aFrom to 1st day of next year
2638             nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 );
2639             aFrom.addMonths( 13 - aFrom.nMonth );
2640 
2641             // move aFrom to 1st day of this year
2642             nDiff += aFrom.getDaysInYearRange( aFrom.nYear, aTo.nYear - 1 );
2643             aFrom.addYears( aTo.nYear - aFrom.nYear );
2644         }
2645 
2646         // move aFrom to 1st day of this month
2647         nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, aTo.nMonth - 1 );
2648         aFrom.addMonths( aTo.nMonth - aFrom.nMonth );
2649     }
2650     // finally add remaining days in this month
2651     nDiff += aTo.nDay - aFrom.nDay;
2652     return std::max<sal_Int32>(nDiff, 0);
2653 }
2654 
operator <(const ScaDate & rCmp) const2655 bool ScaDate::operator<( const ScaDate& rCmp ) const
2656 {
2657     if( nYear != rCmp.nYear )
2658         return nYear < rCmp.nYear;
2659     if( nMonth != rCmp.nMonth )
2660         return nMonth < rCmp.nMonth;
2661     if( nDay != rCmp.nDay )
2662         return nDay < rCmp.nDay;
2663     if( bLastDay || rCmp.bLastDay )
2664         return !bLastDay && rCmp.bLastDay;
2665     return nOrigDay < rCmp.nOrigDay;
2666 }
2667 
2668 
ScaAnyConverter(const uno::Reference<uno::XComponentContext> & xContext)2669 ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext )
2670     : nDefaultFormat(0)
2671     , bHasValidFormat(false)
2672 {
2673     xFormatter = util::NumberFormatter::create(xContext);
2674 }
2675 
~ScaAnyConverter()2676 ScaAnyConverter::~ScaAnyConverter()
2677 {
2678 }
2679 
init(const uno::Reference<beans::XPropertySet> & xPropSet)2680 void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet )
2681 {
2682     // try to get default number format
2683     bHasValidFormat = false;
2684     if( !xFormatter.is() )
2685         return;
2686 
2687     // get XFormatsSupplier from outer XPropertySet
2688     uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY );
2689     if( !xFormatsSupp.is() )
2690         return;
2691 
2692     // get XNumberFormatTypes from XNumberFormatsSupplier to get standard index
2693     uno::Reference< util::XNumberFormats > xFormats( xFormatsSupp->getNumberFormats() );
2694     uno::Reference< util::XNumberFormatTypes > xFormatTypes( xFormats, uno::UNO_QUERY );
2695     if( xFormatTypes.is() )
2696     {
2697         lang::Locale eLocale;
2698         nDefaultFormat = xFormatTypes->getStandardIndex( eLocale );
2699         xFormatter->attachNumberFormatsSupplier( xFormatsSupp );
2700         bHasValidFormat = true;
2701     }
2702 }
2703 
convertToDouble(const OUString & rString) const2704 double ScaAnyConverter::convertToDouble( const OUString& rString ) const
2705 {
2706     double fValue = 0.0;
2707     if( bHasValidFormat )
2708     {
2709         try
2710         {
2711             fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString );
2712         }
2713         catch( uno::Exception& )
2714         {
2715             throw lang::IllegalArgumentException();
2716         }
2717     }
2718     else
2719     {
2720         rtl_math_ConversionStatus eStatus;
2721         sal_Int32 nEnd;
2722         fValue = ::rtl::math::stringToDouble( rString, '.', ',', &eStatus, &nEnd );
2723         if( (eStatus != rtl_math_ConversionStatus_Ok) || (nEnd < rString.getLength()) )
2724             throw lang::IllegalArgumentException();
2725     }
2726     return fValue;
2727 }
2728 
getDouble(double & rfResult,const uno::Any & rAny) const2729 bool ScaAnyConverter::getDouble(
2730         double& rfResult,
2731         const uno::Any& rAny ) const
2732 {
2733     rfResult = 0.0;
2734     bool bContainsVal = true;
2735     switch( rAny.getValueTypeClass() )
2736     {
2737         case uno::TypeClass_VOID:
2738             bContainsVal = false;
2739         break;
2740         case uno::TypeClass_STRING:
2741         {
2742             auto pString = o3tl::forceAccess< OUString >( rAny );
2743             if( !pString->isEmpty() )
2744                 rfResult = convertToDouble( *pString );
2745             else
2746                 bContainsVal = false;
2747         }
2748         break;
2749         case uno::TypeClass_HYPER:
2750             rfResult = rAny.get<sal_Int64>();
2751         break;
2752         case uno::TypeClass_UNSIGNED_HYPER:
2753             rfResult = rAny.get<sal_uInt64>();
2754         break;
2755         default:
2756             if( !( rAny >>= rfResult ) )
2757                 throw lang::IllegalArgumentException();
2758     }
2759 
2760     return bContainsVal;
2761 }
2762 
getDouble(double & rfResult,const uno::Reference<beans::XPropertySet> & xPropSet,const uno::Any & rAny)2763 bool ScaAnyConverter::getDouble(
2764         double& rfResult,
2765         const uno::Reference< beans::XPropertySet >& xPropSet,
2766         const uno::Any& rAny )
2767 {
2768     init( xPropSet );
2769     return getDouble( rfResult, rAny );
2770 }
2771 
getDouble(const uno::Reference<beans::XPropertySet> & xPropSet,const uno::Any & rAny,double fDefault)2772 double ScaAnyConverter::getDouble(
2773         const uno::Reference< beans::XPropertySet >& xPropSet,
2774         const uno::Any& rAny,
2775         double fDefault )
2776 {
2777     double fResult;
2778     if( !getDouble( fResult, xPropSet, rAny ) )
2779         fResult = fDefault;
2780     return fResult;
2781 }
2782 
getInt32(sal_Int32 & rnResult,const uno::Reference<beans::XPropertySet> & xPropSet,const uno::Any & rAny)2783 bool ScaAnyConverter::getInt32(
2784         sal_Int32& rnResult,
2785         const uno::Reference< beans::XPropertySet >& xPropSet,
2786         const uno::Any& rAny )
2787 {
2788     double fResult;
2789     bool bContainsVal = getDouble( fResult, xPropSet, rAny );
2790     if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) )
2791         throw lang::IllegalArgumentException();
2792 
2793     rnResult = static_cast< sal_Int32 >( fResult );
2794     return bContainsVal;
2795 }
2796 
getInt32(const uno::Reference<beans::XPropertySet> & xPropSet,const uno::Any & rAny,sal_Int32 nDefault)2797 sal_Int32 ScaAnyConverter::getInt32(
2798         const uno::Reference< beans::XPropertySet >& xPropSet,
2799         const uno::Any& rAny,
2800         sal_Int32 nDefault )
2801 {
2802     sal_Int32 nResult;
2803     if( !getInt32( nResult, xPropSet, rAny ) )
2804         nResult = nDefault;
2805     return nResult;
2806 }
2807 
2808 }
2809 
2810 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2811