Bump version to 6.0-36
[LibreOffice.git] / scaddins / source / analysis / analysishelper.cxx
blobc66eb30afe9b4d0241f8d6cf64898a903805ad2d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/util/XNumberFormatTypes.hpp>
21 #include <com/sun/star/util/NumberFormatter.hpp>
23 #include <string.h>
24 #include <stdio.h>
25 #include <o3tl/any.hxx>
26 #include <rtl/math.hxx>
27 #include <sal/macros.h>
28 #include <algorithm>
29 #include <memory>
30 #include "analysishelper.hxx"
31 #include <analysis.hrc>
32 #include <strings.hrc>
33 #include "deffuncname.hxx"
35 using namespace ::com::sun::star;
36 using namespace sca::analysis;
38 #define UNIQUE false // function name does not exist in Calc
39 #define DOUBLE true // function name exists in Calc
41 #define STDPAR false // all parameters are described
42 #define INTPAR true // first parameter is internal
44 #define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \
45 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr }
47 #define FUNCDATAS( FUNCNAME, DBL, OPT, NUMOFPAR, CAT, SUFFIX ) \
48 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, SUFFIX }
50 const FuncDataBase pFuncDatas[] =
52 // UNIQUE or INTPAR or
53 // function name DOUBLE STDPAR # of param category
54 FUNCDATA( Workday, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
55 FUNCDATA( Yearfrac, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
56 FUNCDATA( Edate, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
57 FUNCDATAS( Weeknum, DOUBLE, INTPAR, 2, FDCategory::DateTime, "_EXCEL2003" ),
58 FUNCDATA( Eomonth, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
59 FUNCDATAS( Networkdays, DOUBLE, INTPAR, 3, FDCategory::DateTime, "_EXCEL2003" ),
60 FUNCDATA( Iseven, DOUBLE, STDPAR, 1, FDCategory::Inf ),
61 FUNCDATA( Isodd, DOUBLE, STDPAR, 1, FDCategory::Inf ),
62 FUNCDATA( Multinomial, UNIQUE, STDPAR, 1, FDCategory::Math ),
63 FUNCDATA( Seriessum, UNIQUE, STDPAR, 4, FDCategory::Math ),
64 FUNCDATA( Quotient, UNIQUE, STDPAR, 2, FDCategory::Math ),
65 FUNCDATA( Mround, UNIQUE, STDPAR, 2, FDCategory::Math ),
66 FUNCDATA( Sqrtpi, UNIQUE, STDPAR, 1, FDCategory::Math ),
67 FUNCDATA( Randbetween, UNIQUE, STDPAR, 2, FDCategory::Math ),
68 FUNCDATAS( Gcd, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
69 FUNCDATAS( Lcm, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
70 FUNCDATA( Besseli, UNIQUE, STDPAR, 2, FDCategory::Tech ),
71 FUNCDATA( Besselj, UNIQUE, STDPAR, 2, FDCategory::Tech ),
72 FUNCDATA( Besselk, UNIQUE, STDPAR, 2, FDCategory::Tech ),
73 FUNCDATA( Bessely, UNIQUE, STDPAR, 2, FDCategory::Tech ),
74 FUNCDATA( Bin2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
75 FUNCDATA( Bin2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
76 FUNCDATA( Bin2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
77 FUNCDATA( Oct2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
78 FUNCDATA( Oct2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
79 FUNCDATA( Oct2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
80 FUNCDATA( Dec2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
81 FUNCDATA( Dec2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
82 FUNCDATA( Dec2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
83 FUNCDATA( Hex2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
84 FUNCDATA( Hex2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
85 FUNCDATA( Hex2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
86 FUNCDATA( Delta, UNIQUE, INTPAR, 2, FDCategory::Tech ),
87 FUNCDATA( Erf, UNIQUE, INTPAR, 2, FDCategory::Tech ),
88 FUNCDATA( Erfc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
89 FUNCDATA( Gestep, UNIQUE, INTPAR, 2, FDCategory::Tech ),
90 FUNCDATA( Factdouble, UNIQUE, STDPAR, 1, FDCategory::Tech ),
91 FUNCDATA( Imabs, UNIQUE, STDPAR, 1, FDCategory::Tech ),
92 FUNCDATA( Imaginary, UNIQUE, STDPAR, 1, FDCategory::Tech ),
93 FUNCDATA( Impower, UNIQUE, STDPAR, 2, FDCategory::Tech ),
94 FUNCDATA( Imargument, UNIQUE, STDPAR, 1, FDCategory::Tech ),
95 FUNCDATA( Imcos, UNIQUE, STDPAR, 1, FDCategory::Tech ),
96 FUNCDATA( Imdiv, UNIQUE, STDPAR, 2, FDCategory::Tech ),
97 FUNCDATA( Imexp, UNIQUE, STDPAR, 1, FDCategory::Tech ),
98 FUNCDATA( Imconjugate, UNIQUE, STDPAR, 1, FDCategory::Tech ),
99 FUNCDATA( Imln, UNIQUE, STDPAR, 1, FDCategory::Tech ),
100 FUNCDATA( Imlog10, UNIQUE, STDPAR, 1, FDCategory::Tech ),
101 FUNCDATA( Imlog2, UNIQUE, STDPAR, 1, FDCategory::Tech ),
102 FUNCDATA( Improduct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
103 FUNCDATA( Imreal, UNIQUE, STDPAR, 1, FDCategory::Tech ),
104 FUNCDATA( Imsin, UNIQUE, STDPAR, 1, FDCategory::Tech ),
105 FUNCDATA( Imsub, UNIQUE, STDPAR, 2, FDCategory::Tech ),
106 FUNCDATA( Imsqrt, UNIQUE, STDPAR, 1, FDCategory::Tech ),
107 FUNCDATA( Imsum, UNIQUE, INTPAR, 1, FDCategory::Tech ),
108 FUNCDATA( Imtan, UNIQUE, STDPAR, 1, FDCategory::Tech ),
109 FUNCDATA( Imsec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
110 FUNCDATA( Imcsc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
111 FUNCDATA( Imcot, UNIQUE, STDPAR, 1, FDCategory::Tech ),
112 FUNCDATA( Imsinh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
113 FUNCDATA( Imcosh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
114 FUNCDATA( Imsech, UNIQUE, STDPAR, 1, FDCategory::Tech ),
115 FUNCDATA( Imcsch, UNIQUE, STDPAR, 1, FDCategory::Tech ),
116 FUNCDATA( Complex, UNIQUE, STDPAR, 3, FDCategory::Tech ),
117 FUNCDATA( Convert, UNIQUE, STDPAR, 3, FDCategory::Tech ),
118 FUNCDATA( Amordegrc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
119 FUNCDATA( Amorlinc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
120 FUNCDATA( Accrint, UNIQUE, INTPAR, 7, FDCategory::Finance ),
121 FUNCDATA( Accrintm, UNIQUE, INTPAR, 5, FDCategory::Finance ),
122 FUNCDATA( Received, UNIQUE, INTPAR, 5, FDCategory::Finance ),
123 FUNCDATA( Disc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
124 FUNCDATA( Duration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
125 FUNCDATA( Effect, DOUBLE, STDPAR, 2, FDCategory::Finance ),
126 FUNCDATA( Cumprinc, DOUBLE, STDPAR, 6, FDCategory::Finance ),
127 FUNCDATA( Cumipmt, DOUBLE, STDPAR, 6, FDCategory::Finance ),
128 FUNCDATA( Price, UNIQUE, INTPAR, 7, FDCategory::Finance ),
129 FUNCDATA( Pricedisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
130 FUNCDATA( Pricemat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
131 FUNCDATA( Mduration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
132 FUNCDATA( Nominal, DOUBLE, STDPAR, 2, FDCategory::Finance ),
133 FUNCDATA( Dollarfr, UNIQUE, STDPAR, 2, FDCategory::Finance ),
134 FUNCDATA( Dollarde, UNIQUE, STDPAR, 2, FDCategory::Finance ),
135 FUNCDATA( Yield, UNIQUE, INTPAR, 7, FDCategory::Finance ),
136 FUNCDATA( Yielddisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
137 FUNCDATA( Yieldmat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
138 FUNCDATA( Tbilleq, UNIQUE, INTPAR, 3, FDCategory::Finance ),
139 FUNCDATA( Tbillprice, UNIQUE, INTPAR, 3, FDCategory::Finance ),
140 FUNCDATA( Tbillyield, UNIQUE, INTPAR, 3, FDCategory::Finance ),
141 FUNCDATA( Oddfprice, UNIQUE, INTPAR, 9, FDCategory::Finance ),
142 FUNCDATA( Oddfyield, UNIQUE, INTPAR, 9, FDCategory::Finance ),
143 FUNCDATA( Oddlprice, UNIQUE, INTPAR, 8, FDCategory::Finance ),
144 FUNCDATA( Oddlyield, UNIQUE, INTPAR, 8, FDCategory::Finance ),
145 FUNCDATA( Xirr, UNIQUE, INTPAR, 3, FDCategory::Finance ),
146 FUNCDATA( Xnpv, UNIQUE, STDPAR, 3, FDCategory::Finance ),
147 FUNCDATA( Intrate, UNIQUE, INTPAR, 5, FDCategory::Finance ),
148 FUNCDATA( Coupncd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
149 FUNCDATA( Coupdays, UNIQUE, INTPAR, 4, FDCategory::Finance ),
150 FUNCDATA( Coupdaysnc, UNIQUE, INTPAR, 4, FDCategory::Finance ),
151 FUNCDATA( Coupdaybs, UNIQUE, INTPAR, 4, FDCategory::Finance ),
152 FUNCDATA( Couppcd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
153 FUNCDATA( Coupnum, UNIQUE, INTPAR, 4, FDCategory::Finance ),
154 FUNCDATA( Fvschedule, UNIQUE, STDPAR, 2, FDCategory::Finance )
156 #undef FUNCDATA
158 namespace sca { namespace analysis {
160 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
162 if( (nMonth == 2) && IsLeapYear( nYear ) )
163 return 29;
164 static const sal_uInt16 aDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
165 return aDaysInMonth[ nMonth ];
170 * Convert a date to a count of days starting from 01/01/0001
172 * The internal representation of a Date used in this Addin
173 * is the number of days between 01/01/0001 and the date
174 * this function converts a Day , Month, Year representation
175 * to this internal Date value.
179 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
181 sal_Int32 nDays = ((sal_Int32)nYear-1) * 365;
182 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
184 for( sal_uInt16 i = 1; i < nMonth; i++ )
185 nDays += DaysInMonth(i,nYear);
186 nDays += nDay;
188 return nDays;
193 * Convert a count of days starting from 01/01/0001 to a date
195 * The internal representation of a Date used in this Addin
196 * is the number of days between 01/01/0001 and the date
197 * this function converts this internal Date value
198 * to a Day , Month, Year representation of a Date.
202 void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
204 if( nDays < 0 )
205 throw lang::IllegalArgumentException();
207 sal_Int32 nTempDays;
208 sal_Int32 i = 0;
209 bool bCalc;
213 nTempDays = nDays;
214 rYear = (sal_uInt16)((nTempDays / 365) - i);
215 nTempDays -= ((sal_Int32) rYear -1) * 365;
216 nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
217 bCalc = false;
218 if ( nTempDays < 1 )
220 i++;
221 bCalc = true;
223 else
225 if ( nTempDays > 365 )
227 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
229 i--;
230 bCalc = true;
235 while ( bCalc );
237 rMonth = 1;
238 while ( nTempDays > DaysInMonth( rMonth, rYear ) )
240 nTempDays -= DaysInMonth( rMonth, rYear );
241 rMonth++;
243 rDay = (sal_uInt16)nTempDays;
248 * Get the null date used by the spreadsheet document
250 * The internal representation of a Date used in this Addin
251 * is the number of days between 01/01/0001 and the date
252 * this function returns this internal Date value for the document null date
256 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt )
258 if( xOpt.is() )
262 uno::Any aAny = xOpt->getPropertyValue( "NullDate" );
263 util::Date aDate;
264 if( aAny >>= aDate )
265 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
267 catch( uno::Exception& )
272 // no null date available -> no calculations possible
273 throw uno::RuntimeException();
277 sal_Int32 GetDiffDate360(
278 sal_uInt16 nDay1, sal_uInt16 nMonth1, sal_uInt16 nYear1, bool bLeapYear1,
279 sal_uInt16 nDay2, sal_uInt16 nMonth2, sal_uInt16 nYear2,
280 bool bUSAMethod )
282 if( nDay1 == 31 )
283 nDay1--;
284 else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) )
285 nDay1 = 30;
287 if( nDay2 == 31 )
289 if( bUSAMethod && nDay1 != 30 )
291 nDay2 = 1;
292 if( nMonth2 == 12 )
294 nYear2++;
295 nMonth2 = 1;
297 else
298 nMonth2++;
300 else
301 nDay2 = 30;
304 return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360;
308 sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod )
310 nDate1 += nNullDate;
311 nDate2 += nNullDate;
313 sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;
315 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
316 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
318 return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod );
322 sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 )
324 sal_uInt16 nLeaps = 0;
325 for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ )
327 if( IsLeapYear( n ) )
328 nLeaps++;
331 sal_uInt32 nSum = 1;
332 nSum += nYear2;
333 nSum -= nYear1;
334 nSum *= 365;
335 nSum += nLeaps;
337 return nSum;
341 sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode,
342 sal_Int32* pOptDaysIn1stYear )
344 bool bNeg = nStartDate > nEndDate;
346 if( bNeg )
348 sal_Int32 n = nEndDate;
349 nEndDate = nStartDate;
350 nStartDate = n;
353 sal_Int32 nRet;
355 switch( nMode )
357 case 0: // 0=USA (NASD) 30/360
358 case 4: // 4=Europe 30/360
360 sal_uInt16 nD1, nM1, nY1, nD2, nM2, nY2;
362 nStartDate += nNullDate;
363 nEndDate += nNullDate;
365 DaysToDate( nStartDate, nD1, nM1, nY1 );
366 DaysToDate( nEndDate, nD2, nM2, nY2 );
368 bool bLeap = IsLeapYear( nY1 );
369 sal_Int32 nDays, nMonths;
371 nMonths = nM2 - nM1;
372 nDays = nD2 - nD1;
374 nMonths += ( nY2 - nY1 ) * 12;
376 nRet = nMonths * 30 + nDays;
377 if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )
378 nRet -= bLeap? 1 : 2;
380 if( pOptDaysIn1stYear )
381 *pOptDaysIn1stYear = 360;
383 break;
384 case 1: // 1=exact/exact
385 if( pOptDaysIn1stYear )
387 sal_uInt16 nD, nM, nY;
389 DaysToDate( nStartDate + nNullDate, nD, nM, nY );
391 *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;
393 nRet = nEndDate - nStartDate;
394 break;
395 case 2: // 2=exact/360
396 nRet = nEndDate - nStartDate;
397 if( pOptDaysIn1stYear )
398 *pOptDaysIn1stYear = 360;
399 break;
400 case 3: //3=exact/365
401 nRet = nEndDate - nStartDate;
402 if( pOptDaysIn1stYear )
403 *pOptDaysIn1stYear = 365;
404 break;
405 default:
406 throw lang::IllegalArgumentException();
409 return bNeg? -nRet : nRet;
413 double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
415 sal_Int32 nDays1stYear;
416 sal_Int32 nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear );
418 return double( nTotalDays ) / double( nDays1stYear );
422 sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode )
424 switch( nMode )
426 case 0: // 0=USA (NASD) 30/360
427 case 2: // 2=exact/360
428 case 4: // 4=Europe 30/360
429 return 360;
430 case 1: // 1=exact/exact
432 sal_uInt16 nD, nM, nY;
433 nDate += nNullDate;
434 DaysToDate( nDate, nD, nM, nY );
435 return IsLeapYear( nY )? 366 : 365;
437 case 3: //3=exact/365
438 return 365;
439 default:
440 throw lang::IllegalArgumentException();
445 //fdo40100 toDo: make function fully compliant with ODFF1.2
446 // LEM: I fixed case nMode==1; anything else to fix?
448 * Function GetYearFrac implements YEARFRAC as defined in:
449 * Open Document Format for Office Applications version 1.2 Part 2, par. 6.10.24
450 * The calculations are defined in:
451 * Open Document Format for Office Applications version 1.2 Part 2, par. 4.11.7
453 double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
455 if( nStartDate == nEndDate )
456 return 0.0; // nothing to do...
458 if( nStartDate > nEndDate )
460 sal_Int32 n = nEndDate;
461 nEndDate = nStartDate;
462 nStartDate = n;
465 sal_Int32 nDate1 = nStartDate + nNullDate;
466 sal_Int32 nDate2 = nEndDate + nNullDate;
468 sal_uInt16 nDay1, nDay2;
469 sal_uInt16 nMonth1, nMonth2;
470 sal_uInt16 nYear1, nYear2;
472 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
473 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
475 // calculate days between nDate1 and nDate2
476 sal_Int32 nDayDiff;
477 switch( nMode )
479 case 0: // 0=USA (NASD) 30/360
480 if ( nDay1 == 31 )
482 nDay1--;
484 if ( nDay1 == 30 && nDay2 == 31 )
486 nDay2--;
488 else
490 if ( nMonth1 == 2 && nDay1 == ( IsLeapYear( nYear1 ) ? 29 : 28 ) )
492 nDay1 = 30;
493 if ( nMonth2 == 2 && nDay2 == ( IsLeapYear( nYear2 ) ? 29 : 28 ) )
495 nDay2 = 30;
499 nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
500 break;
501 case 1: // 1=exact/exact
502 case 2: // 2=exact/360
503 case 3: // 3=exact/365
504 nDayDiff = nDate2 - nDate1;
505 break;
506 case 4: // 4=Europe 30/360
507 if ( nDay1 == 31 )
509 nDay1--;
511 if ( nDay2 == 31 )
513 nDay2--;
515 nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
516 break;
517 default:
518 throw lang::IllegalArgumentException();
521 //calculate days in year
522 double nDaysInYear;
523 switch( nMode )
525 case 0: // 0=USA (NASD) 30/360
526 case 2: // 2=exact/360
527 case 4: // 4=Europe 30/360
528 nDaysInYear = 360;
529 break;
530 case 1: // 1=exact/exact
532 const bool isYearDifferent = ( nYear1 != nYear2 );
533 // ODFv1.2 part 2 section 4.11.7.7.7
534 if ( isYearDifferent &&
535 ( ( nYear2 != nYear1 + 1 ) ||
536 ( nMonth1 < nMonth2 ) ||
537 ( nMonth1 == nMonth2 && nDay1 < nDay2 ) ) )
539 // return average of days in year between nDate1 and nDate2, inclusive
540 sal_Int32 nDayCount = 0;
541 for ( sal_Int16 i = nYear1; i <= nYear2; i++ )
542 nDayCount += ( IsLeapYear( i ) ? 366 : 365 );
544 nDaysInYear = ( double ) nDayCount / ( double ) ( nYear2 - nYear1 + 1 );
546 // we take advantage of the fact that (ODFv1.2 part 2) 4.11.7.7.9
547 // 4.11.7.7.10 can be permuted without changing the end result
548 // ODFv1.2 part 2 section 4.11.7.7.8 and 4.11.7.7.10
549 else if ( ( isYearDifferent && IsLeapYear( nYear1 ) ) ||
550 ( nMonth2 == 2 && nDay2 == 29) )
552 nDaysInYear = 366;
554 else
556 // ODFv1.2 part 2 section 4.11.7.7.9:
557 // we need to determine whether there is a 29 February
558 // between nDate1 and nDate2
559 // LEM FIXME: I have a doubt concerning nDate1 == "29 February YYYY"
560 // In this case, is the "29 February YYYY" between nDate1 and nDate2
561 // in the meaning of ODFv1.2 part 2, section 4.11.7.7.9?
562 // I assume "no", since if "between" is to be understood as "inclusive"
563 // then 4.11.7.7.10 has no point.
564 // OTOH, it could theoretically be possible that "between"
565 // is to be understood as "inclusive the lower bound, exclusive in upper bound".
567 assert(nYear1 == nYear2 || nYear1 + 1 == nYear2);
568 // as a consequence, nYearDifferent iff nYear2 == nYear + 1, and
569 // there are only two possible 29 Februaries to consider:
570 // "29 February nYear1" and "29 February nYear2"
572 // nDate2=="29 February YYYY" is handled above and the following conditions
573 // rely on that for simplification.
574 assert( ! (nMonth2 == 2 && nDay2 == 29));
576 if( IsLeapYear( nYear1 ) )
577 assert(nYear1 == nYear2);
579 // is 29/2/nYear1 between nDate1 and nDate2?
580 // that is only possible if IsLeapYear( nYear1 ),
581 // which implies nYear1 == nYear2
582 if( IsLeapYear( nYear1 ) &&
583 ( nMonth1 == 1 || ( nMonth1 == 2 && nDay1 <= 28 )) &&
584 nMonth2 > 2 )
586 nDaysInYear = 366;
588 // is 29/2/nYear2 between nDate1 and nDate2?
589 // if nYear1==nYear2, then that is adequately tested by the previous test,
590 // so no need to retest it here.
591 else if(isYearDifferent && nMonth2 > 2 && IsLeapYear( nYear2 ))
593 nDaysInYear = 366;
595 else
597 assert( !( IsLeapYear( nYear2 ) &&
598 nYear1 == nYear2 &&
599 (nMonth1 == 1 || (nMonth1==2 && nDay1 <= 28)) &&
600 nMonth2 > 2));
601 nDaysInYear = 365;
605 break;
606 case 3: // 3=exact/365
607 nDaysInYear = 365;
608 break;
609 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
610 default:
611 throw lang::IllegalArgumentException();
614 return double( nDayDiff ) / nDaysInYear;
617 double BinomialCoefficient( double n, double k )
619 // This method is a copy of BinomKoeff()
620 // found in sc/source/core/tool/interpr3.cxx
622 double nVal = 0.0;
623 k = ::rtl::math::approxFloor(k);
624 if (n < k)
625 nVal = 0.0;
626 else if (k == 0.0)
627 nVal = 1.0;
628 else
630 nVal = n/k;
631 n--;
632 k--;
633 while (k > 0.0)
635 nVal *= n/k;
636 k--;
637 n--;
640 return nVal;
643 double GetGcd( double f1, double f2 )
645 double f = fmod( f1, f2 );
646 while( f > 0.0 )
648 f1 = f2;
649 f2 = f;
650 f = fmod( f1, f2 );
653 return f2;
657 double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim )
659 if ( nBase < 2 || nBase > 36 )
660 throw lang::IllegalArgumentException();
662 sal_uInt32 nStrLen = aStr.getLength();
663 if( nStrLen > nCharLim )
664 throw lang::IllegalArgumentException();
665 else if( !nStrLen )
666 return 0.0;
668 double fVal = 0.0;
670 const sal_Unicode* p = aStr.getStr();
672 sal_uInt16 nFirstDig = 0;
673 bool bFirstDig = true;
674 double fBase = nBase;
676 while ( *p )
678 sal_uInt16 n;
680 if( '0' <= *p && *p <= '9' )
681 n = *p - '0';
682 else if( 'A' <= *p && *p <= 'Z' )
683 n = 10 + ( *p - 'A' );
684 else if ( 'a' <= *p && *p <= 'z' )
685 n = 10 + ( *p - 'a' );
686 else
687 n = nBase;
689 if( n >= nBase )
690 throw lang::IllegalArgumentException(); // illegal char!
692 if( bFirstDig )
694 bFirstDig = false;
695 nFirstDig = n;
697 fVal = fVal * fBase + double( n );
699 p++;
703 if( nStrLen == nCharLim && !bFirstDig && (nFirstDig >= nBase / 2) )
704 { // handling negativ values
705 fVal = ( pow( double( nBase ), double( nCharLim ) ) - fVal ); // complement
706 fVal *= -1.0;
709 return fVal;
713 static inline sal_Char GetMaxChar( sal_uInt16 nBase )
715 const sal_Char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
716 return c[ nBase ];
720 OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase,
721 sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces )
723 fNum = ::rtl::math::approxFloor( fNum );
724 fMin = ::rtl::math::approxFloor( fMin );
725 fMax = ::rtl::math::approxFloor( fMax );
727 if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) )
728 throw lang::IllegalArgumentException();
730 sal_Int64 nNum = static_cast< sal_Int64 >( fNum );
731 bool bNeg = nNum < 0;
732 if( bNeg )
733 nNum = sal_Int64( pow( double( nBase ), double( nMaxPlaces ) ) ) + nNum;
735 OUString aRet( OUString::number( nNum, nBase ).toAsciiUpperCase() );
738 if( bUsePlaces )
740 sal_Int32 nLen = aRet.getLength();
741 if( !bNeg && nLen > nPlaces )
743 throw lang::IllegalArgumentException();
745 else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) )
747 sal_Int32 nLeft = nPlaces - nLen;
748 std::unique_ptr<sal_Char[]> p( new sal_Char[ nLeft + 1 ] );
749 memset( p.get(), bNeg ? GetMaxChar( nBase ) : '0', nLeft );
750 p[ nLeft ] = 0x00;
751 OUString aTmp( p.get(), nLeft, RTL_TEXTENCODING_MS_1252 );
752 aTmp += aRet;
753 aRet = aTmp;
757 return aRet;
760 // implementation moved to module sal, see #i97091#
761 double Erf( double x )
763 return ::rtl::math::erf(x);
766 // implementation moved to module sal, see #i97091#
767 double Erfc( double x )
769 return ::rtl::math::erfc(x);
772 inline bool IsNum( sal_Unicode c )
774 return c >= '0' && c <= '9';
778 inline bool IsComma( sal_Unicode c )
780 return c == '.' || c == ',';
784 inline bool IsExpStart( sal_Unicode c )
786 return c == 'e' || c == 'E';
790 inline bool IsImagUnit( sal_Unicode c )
792 return c == 'i' || c == 'j';
796 inline sal_uInt16 GetVal( sal_Unicode c )
798 return sal_uInt16( c - '0' );
802 bool ParseDouble( const sal_Unicode*& rp, double& rRet )
804 double fInt = 0.0;
805 double fFrac = 0.0;
806 double fMult = 0.1; // multiplier to multiply digits with, when adding fractional ones
807 sal_Int32 nExp = 0;
808 sal_Int32 nMaxExp = 307;
809 sal_uInt16 nDigCnt = 18; // max. number of digits to read in, rest doesn't matter
811 enum State { S_End = 0, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp };
813 State eS = S_Sign;
815 bool bNegNum = false;
816 bool bNegExp = false;
818 const sal_Unicode* p = rp;
819 sal_Unicode c;
821 while( eS )
823 c = *p;
824 switch( eS )
826 case S_Sign:
827 if( IsNum( c ) )
829 fInt = GetVal( c );
830 nDigCnt--;
831 eS = S_Int;
833 else if( c == '-' )
835 bNegNum = true;
836 eS = S_IntStart;
838 else if( c == '+' )
839 eS = S_IntStart;
840 else if( IsComma( c ) )
841 eS = S_Frac;
842 else
843 return false;
844 break;
845 case S_IntStart:
846 if( IsNum( c ) )
848 fInt = GetVal( c );
849 nDigCnt--;
850 eS = S_Int;
852 else if( IsComma( c ) )
853 eS = S_Frac;
854 else if( IsImagUnit( c ) )
856 rRet = 0.0;
857 return true;
859 else
860 return false;
861 break;
862 case S_Int:
863 if( IsNum( c ) )
865 fInt *= 10.0;
866 fInt += double( GetVal( c ) );
867 nDigCnt--;
868 if( !nDigCnt )
869 eS = S_IgnoreIntDigs;
871 else if( IsComma( c ) )
872 eS = S_Frac;
873 else if( IsExpStart( c ) )
874 eS = S_ExpSign;
875 else
876 eS = S_End;
877 break;
878 case S_IgnoreIntDigs:
879 if( IsNum( c ) )
880 nExp++; // just multiply num with 10... ;-)
881 else if( IsComma( c ) )
882 eS = S_Frac;
883 else if( IsExpStart( c ) )
884 eS = S_ExpSign;
885 else
886 eS = S_End;
887 break;
888 case S_Frac:
889 if( IsNum( c ) )
891 fFrac += double( GetVal( c ) ) * fMult;
892 nDigCnt--;
893 if( nDigCnt )
894 fMult *= 0.1;
895 else
896 eS = S_IgnoreFracDigs;
898 else if( IsExpStart( c ) )
899 eS = S_ExpSign;
900 else
901 eS = S_End;
902 break;
903 case S_IgnoreFracDigs:
904 if( IsExpStart( c ) )
905 eS = S_ExpSign;
906 else if( !IsNum( c ) )
907 eS = S_End;
908 break;
909 case S_ExpSign:
910 if( IsNum( c ) )
912 nExp = GetVal( c );
913 eS = S_Exp;
915 else if( c == '-' )
917 bNegExp = true;
918 eS = S_Exp;
920 else if( c != '+' )
921 eS = S_End;
922 break;
923 case S_Exp:
924 if( IsNum( c ) )
926 nExp *= 10;
927 nExp += GetVal( c );
928 if( nExp > nMaxExp )
929 return false;
931 else
932 eS = S_End;
933 break;
934 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
935 case S_End:
936 break;
939 p++;
942 p--; // set pointer back to last
943 rp = p;
945 fInt += fFrac;
947 if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error
949 sal_Int32 nLog10 = sal_Int32( log10( fInt ) );
951 if( bNegExp )
952 nExp = -nExp;
954 if( nLog10 + nExp > nMaxExp )
955 return false;
957 fInt = ::rtl::math::pow10Exp( fInt, nExp );
960 if( bNegNum )
961 fInt = -fInt;
963 rRet = fInt;
965 return true;
969 OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig )
971 const int nBuff = 256;
972 sal_Char aBuff[ nBuff + 1 ];
973 const char* pFormStr = bLeadingSign? "%+.*g" : "%.*g";
974 int nLen = snprintf( aBuff, nBuff, pFormStr, int( nMaxDig ), f );
975 // you never know which underlying implementation you get ...
976 aBuff[nBuff] = 0;
977 if ( nLen < 0 || nLen > nBuff )
978 nLen = strlen( aBuff );
980 OUString aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 );
982 return aRet;
986 double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
987 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
989 sal_uInt32 nPer = sal_uInt32( fPer );
990 double fUsePer = 1.0 / fRate;
991 double fAmorCoeff;
993 if( fUsePer < 3.0 )
994 fAmorCoeff = 1.0;
995 else if( fUsePer < 5.0 )
996 fAmorCoeff = 1.5;
997 else if( fUsePer <= 6.0 )
998 fAmorCoeff = 2.0;
999 else
1000 fAmorCoeff = 2.5;
1002 fRate *= fAmorCoeff;
1003 double fNRate = ::rtl::math::round( GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost );
1004 fCost -= fNRate;
1005 double fRest = fCost - fRestVal; // aboriginal cost - residual value - sum of all write-downs
1007 for( sal_uInt32 n = 0 ; n < nPer ; n++ )
1009 fNRate = ::rtl::math::round( fRate * fCost );
1010 fRest -= fNRate;
1012 if( fRest < 0.0 )
1014 switch( nPer - n )
1016 case 0:
1017 case 1:
1018 return ::rtl::math::round( fCost * 0.5 );
1019 default:
1020 return 0.0;
1024 fCost -= fNRate;
1027 return fNRate;
1031 double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
1032 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
1034 sal_uInt32 nPer = sal_uInt32( fPer );
1035 double fOneRate = fCost * fRate;
1036 double fCostDelta = fCost - fRestVal;
1037 double f0Rate = GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost;
1038 sal_uInt32 nNumOfFullPeriods = sal_uInt32( ( fCost - fRestVal - f0Rate) / fOneRate );
1040 double fResult = 0.0;
1041 if( nPer == 0 )
1042 fResult = f0Rate;
1043 else if( nPer <= nNumOfFullPeriods )
1044 fResult = fOneRate;
1045 else if( nPer == nNumOfFullPeriods + 1 )
1046 fResult = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
1048 if ( fResult > 0.0 )
1049 return fResult;
1050 else
1051 return 0.0;
1055 double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup,
1056 double fYield, sal_Int32 nFreq, sal_Int32 nBase )
1058 double fYearfrac = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1059 double fNumOfCoups = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1060 double fDur = 0.0;
1061 const double f100 = 100.0;
1062 fCoup *= f100 / double( nFreq ); // fCoup is used as cash flow
1063 fYield /= nFreq;
1064 fYield += 1.0;
1066 double nDiff = fYearfrac * nFreq - fNumOfCoups;
1068 double t;
1070 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1071 fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff );
1073 fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1075 double p = 0.0;
1076 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1077 p += fCoup / pow( fYield, t + nDiff );
1079 p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1081 fDur /= p;
1082 fDur /= double( nFreq );
1084 return fDur;
1088 double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue,
1089 double fRate, double fPrice, sal_Int32 nBase )
1091 double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );
1092 double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );
1093 double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1095 double y = 1.0 + fIssMat * fRate;
1096 y /= fPrice / 100.0 + fIssSet * fRate;
1097 y--;
1098 y /= fSetMat;
1100 return y;
1104 double GetOddfprice( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1105 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fYield*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1106 sal_Int32 /*nBase*/ )
1108 // If you change this to not unconditionally throw, the
1109 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1110 // financial.cxx can be removed.
1111 throw uno::RuntimeException();
1115 double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice,
1116 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1118 double fRate = fCoup;
1119 double fPriceN = 0.0;
1120 double fYield1 = 0.0;
1121 double fYield2 = 1.0;
1122 double fPrice1 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield1, fRedemp, nFreq, nBase );
1123 double fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1124 double fYieldN = ( fYield2 - fYield1 ) * 0.5;
1126 for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ )
1128 fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase );
1130 if( rtl::math::approxEqual(fPrice, fPrice1) )
1131 return fYield1;
1132 else if( rtl::math::approxEqual(fPrice, fPrice2) )
1133 return fYield2;
1134 else if( rtl::math::approxEqual(fPrice, fPriceN) )
1135 return fYieldN;
1136 else if( fPrice < fPrice2 )
1138 fYield2 *= 2.0;
1139 fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1141 fYieldN = ( fYield2 - fYield1 ) * 0.5;
1143 else
1145 if( fPrice < fPriceN )
1147 fYield1 = fYieldN;
1148 fPrice1 = fPriceN;
1150 else
1152 fYield2 = fYieldN;
1153 fPrice2 = fPriceN;
1156 fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) );
1160 if( fabs( fPrice - fPriceN ) > fPrice / 100.0 )
1161 throw lang::IllegalArgumentException(); // result not precise enough
1163 return fYieldN;
1167 double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield,
1168 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1170 double fFreq = nFreq;
1172 double fE = GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase );
1173 double fDSC_E = GetCoupdaysnc( nNullDate, nSettle, nMat, nFreq, nBase ) / fE;
1174 double fN = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1175 double fA = GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1177 double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) );
1178 fRet -= 100.0 * fRate / fFreq * fA / fE;
1180 double fT1 = 100.0 * fRate / fFreq;
1181 double fT2 = 1.0 + fYield / fFreq;
1183 for( double fK = 0.0 ; fK < fN ; fK++ )
1184 fRet += fT1 / pow( fT2, fK + fDSC_E );
1186 return fRet;
1190 double GetOddfyield( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1191 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fPrice*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1192 sal_Int32 /*nBase*/ )
1194 // If you change this to not unconditionally throw, the
1195 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1196 // financial.cxx can be removed.
1197 throw uno::RuntimeException();
1201 double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1202 double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1204 double fFreq = double( nFreq );
1205 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1206 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1207 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1209 double p = fRedemp + fDCi * 100.0 * fRate / fFreq;
1210 p /= fDSCi * fYield / fFreq + 1.0;
1211 p -= fAi * 100.0 * fRate / fFreq;
1213 return p;
1217 double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1218 double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1220 double fFreq = double( nFreq );
1221 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1222 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1223 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1225 double y = fRedemp + fDCi * 100.0 * fRate / fFreq;
1226 y /= fPrice + fAi * 100.0 * fRate / fFreq;
1227 y--;
1228 y *= fFreq / fDSCi;
1230 return y;
1234 double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType )
1236 double fPmt;
1237 if( fRate == 0.0 )
1238 fPmt = ( fPv + fFv ) / fNper;
1239 else
1241 double fTerm = pow( 1.0 + fRate, fNper );
1242 if( nPayType > 0 )
1243 fPmt = ( fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fRate );
1244 else
1245 fPmt = fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm );
1248 return -fPmt;
1252 double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType )
1254 double fFv;
1255 if( fRate == 0.0 )
1256 fFv = fPv + fPmt * fNper;
1257 else
1259 double fTerm = pow( 1.0 + fRate, fNper );
1260 if( nPayType > 0 )
1261 fFv = fPv * fTerm + fPmt * ( 1.0 + fRate ) * ( fTerm - 1.0 ) / fRate;
1262 else
1263 fFv = fPv * fTerm + fPmt * ( fTerm - 1.0 ) / fRate;
1266 return -fFv;
1269 // financial functions COUP***
1271 // COUPPCD: find last coupon date before settlement (can be equal to settlement)
1272 /// @throws css::lang::IllegalArgumentException
1273 static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1275 rDate = rMat;
1276 rDate.setYear( rSettle.getYear() );
1277 if( rDate < rSettle )
1278 rDate.addYears( 1 );
1279 while( rDate > rSettle )
1280 rDate.addMonths( -12 / nFreq );
1283 double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1285 if( nSettle >= nMat || CHK_Freq )
1286 throw lang::IllegalArgumentException();
1288 ScaDate aDate;
1289 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1290 return aDate.getDate( nNullDate );
1293 // COUPNCD: find first coupon date after settlement (is never equal to settlement)
1294 /// @throws css::lang::IllegalArgumentException
1295 static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1297 rDate = rMat;
1298 rDate.setYear( rSettle.getYear() );
1299 if( rDate > rSettle )
1300 rDate.addYears( -1 );
1301 while( rDate <= rSettle )
1302 rDate.addMonths( 12 / nFreq );
1305 double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1307 if( nSettle >= nMat || CHK_Freq )
1308 throw lang::IllegalArgumentException();
1310 ScaDate aDate;
1311 lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1312 return aDate.getDate( nNullDate );
1315 // COUPDAYBS: get day count: coupon date before settlement <-> settlement
1316 double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1318 if( nSettle >= nMat || CHK_Freq )
1319 throw lang::IllegalArgumentException();
1321 ScaDate aSettle( nNullDate, nSettle, nBase );
1322 ScaDate aDate;
1323 lcl_GetCouppcd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1324 return ScaDate::getDiff( aDate, aSettle );
1327 // COUPDAYSNC: get day count: settlement <-> coupon date after settlement
1328 double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1330 if( nSettle >= nMat || CHK_Freq )
1331 throw lang::IllegalArgumentException();
1333 if( (nBase != 0) && (nBase != 4) )
1335 ScaDate aSettle( nNullDate, nSettle, nBase );
1336 ScaDate aDate;
1337 lcl_GetCoupncd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1338 return ScaDate::getDiff( aSettle, aDate );
1340 return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1343 // COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement
1344 double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1346 if( nSettle >= nMat || CHK_Freq )
1347 throw lang::IllegalArgumentException();
1349 if( nBase == 1 )
1351 ScaDate aDate;
1352 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1353 ScaDate aNextDate( aDate );
1354 aNextDate.addMonths( 12 / nFreq );
1355 return ScaDate::getDiff( aDate, aNextDate );
1357 return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq;
1360 // COUPNUM: get count of coupon dates
1361 double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1363 if( nSettle >= nMat || CHK_Freq )
1364 throw lang::IllegalArgumentException();
1366 ScaDate aMat( nNullDate, nMat, nBase );
1367 ScaDate aDate;
1368 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), aMat, nFreq );
1369 sal_uInt16 nMonths = (aMat.getYear() - aDate.getYear()) * 12 + aMat.getMonth() - aDate.getMonth();
1370 return static_cast< double >( nMonths * nFreq / 12 );
1373 FuncData::FuncData(const FuncDataBase& r) :
1374 aIntName( OUString::createFromAscii( r.pIntName ) ),
1375 pUINameID( r.pUINameID ),
1376 pDescrID( r.pDescrID ),
1377 bDouble( r.bDouble ),
1378 bWithOpt( r.bWithOpt ),
1379 nParam( r.nNumOfParams ),
1380 eCat( r.eCat )
1382 if (r.pSuffix)
1383 aSuffix = OUString::createFromAscii(r.pSuffix);
1385 aCompList.resize(2);
1386 aCompList[0] = OUString(r.pCompListID[0], strlen(r.pCompListID[0]), RTL_TEXTENCODING_UTF8);
1387 aCompList[1] = OUString(r.pCompListID[1], strlen(r.pCompListID[1]), RTL_TEXTENCODING_UTF8);
1390 FuncData::~FuncData()
1394 sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const
1396 if( !bWithOpt )
1397 nParamNum++;
1399 if( nParamNum > nParam )
1400 return nParam * 2;
1401 else
1402 return nParamNum * 2;
1405 void InitFuncDataList(FuncDataList& rList)
1407 for(const auto & rFuncData : pFuncDatas)
1408 rList.push_back(FuncData(rFuncData));
1411 SortedIndividualInt32List::SortedIndividualInt32List()
1416 SortedIndividualInt32List::~SortedIndividualInt32List()
1421 void SortedIndividualInt32List::Insert( sal_Int32 nDay )
1423 sal_uInt32 nIndex = Count();
1424 while( nIndex )
1426 nIndex--;
1427 sal_Int32 nRef = Get( nIndex );
1428 if( nDay == nRef )
1429 return;
1430 else if( nDay > nRef )
1432 maVector.insert( maVector.begin() + nIndex + 1, nDay );
1433 return;
1436 maVector.insert( maVector.begin(), nDay );
1440 void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1442 if( !nDay )
1443 return;
1445 nDay += nNullDate;
1446 if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) )
1447 Insert( nDay );
1451 void SortedIndividualInt32List::Insert(
1452 double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1454 if( (fDay < -2147483648.0) || (fDay > 2147483649.0) )
1455 throw lang::IllegalArgumentException();
1456 Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend );
1460 bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const
1462 sal_uInt32 nE = Count();
1464 if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) )
1465 return false;
1467 // linear search
1469 for( sal_uInt32 n = 0 ; n < nE ; n++ )
1471 sal_Int32 nRef = Get( n );
1473 if( nRef == nVal )
1474 return true;
1475 else if( nRef > nVal )
1476 return false;
1478 return false;
1482 void SortedIndividualInt32List::InsertHolidayList(
1483 const ScaAnyConverter& rAnyConv,
1484 const uno::Any& rHolAny,
1485 sal_Int32 nNullDate,
1486 bool bInsertOnWeekend )
1488 double fDay;
1489 if( rAnyConv.getDouble( fDay, rHolAny ) )
1490 Insert( fDay, nNullDate, bInsertOnWeekend );
1494 void SortedIndividualInt32List::InsertHolidayList(
1495 ScaAnyConverter& rAnyConv,
1496 const uno::Reference< beans::XPropertySet >& xOptions,
1497 const uno::Any& rHolAny,
1498 sal_Int32 nNullDate )
1500 rAnyConv.init( xOptions );
1501 if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE )
1503 uno::Sequence< uno::Sequence< uno::Any > > aAnySeq;
1504 if( !(rHolAny >>= aAnySeq) )
1505 throw lang::IllegalArgumentException();
1507 const uno::Sequence< uno::Any >* pSeqArray = aAnySeq.getConstArray();
1508 for( sal_Int32 nIndex1 = 0; nIndex1 < aAnySeq.getLength(); nIndex1++ )
1510 const uno::Sequence< uno::Any >& rSubSeq = pSeqArray[ nIndex1 ];
1511 const uno::Any* pAnyArray = rSubSeq.getConstArray();
1513 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1514 InsertHolidayList( rAnyConv, pAnyArray[ nIndex2 ], nNullDate, false/*bInsertOnWeekend*/ );
1517 else
1518 InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ );
1522 void ScaDoubleList::Append(
1523 const uno::Sequence< uno::Sequence< double > >& rValueSeq )
1525 const uno::Sequence< double >* pSeqArray = rValueSeq.getConstArray();
1526 for( sal_Int32 nIndex1 = 0; nIndex1 < rValueSeq.getLength(); nIndex1++ )
1528 const uno::Sequence< double >& rSubSeq = pSeqArray[ nIndex1 ];
1529 const double* pArray = rSubSeq.getConstArray();
1530 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1531 Append( pArray[ nIndex2 ] );
1536 void ScaDoubleList::Append(
1537 const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq )
1539 const uno::Sequence< sal_Int32 >* pSeqArray = rValueSeq.getConstArray();
1540 for( sal_Int32 nIndex1 = 0; nIndex1 < rValueSeq.getLength(); nIndex1++ )
1542 const uno::Sequence< sal_Int32 >& rSubSeq = pSeqArray[ nIndex1 ];
1543 const sal_Int32* pArray = rSubSeq.getConstArray();
1544 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1545 Append( pArray[ nIndex2 ] );
1549 void ScaDoubleList::Append(
1550 const ScaAnyConverter& rAnyConv,
1551 const uno::Any& rAny,
1552 bool bIgnoreEmpty )
1554 if( auto s = o3tl::tryAccess<
1555 css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) )
1556 Append( rAnyConv, *s, bIgnoreEmpty );
1557 else
1559 double fValue;
1560 if( rAnyConv.getDouble( fValue, rAny ) )
1561 Append( fValue );
1562 else if( !bIgnoreEmpty )
1563 Append( 0.0 );
1568 void ScaDoubleList::Append(
1569 const ScaAnyConverter& rAnyConv,
1570 const uno::Sequence< uno::Any >& rAnySeq,
1571 bool bIgnoreEmpty )
1573 const uno::Any* pArray = rAnySeq.getConstArray();
1574 for( sal_Int32 nIndex = 0; nIndex < rAnySeq.getLength(); nIndex++ )
1575 Append( rAnyConv, pArray[ nIndex ], bIgnoreEmpty );
1579 void ScaDoubleList::Append(
1580 const ScaAnyConverter& rAnyConv,
1581 const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq,
1582 bool bIgnoreEmpty )
1584 const uno::Sequence< uno::Any >* pArray = rAnySeq.getConstArray();
1585 for( sal_Int32 nIndex = 0; nIndex < rAnySeq.getLength(); nIndex++ )
1586 Append( rAnyConv, pArray[ nIndex ], bIgnoreEmpty );
1589 void ScaDoubleList::Append(
1590 ScaAnyConverter& rAnyConv,
1591 const uno::Reference< beans::XPropertySet >& xOpt,
1592 const uno::Sequence< uno::Any >& rAnySeq )
1594 rAnyConv.init( xOpt );
1595 Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ );
1599 bool ScaDoubleList::CheckInsert( double ) const
1601 return true;
1605 bool ScaDoubleListGT0::CheckInsert( double fValue ) const
1607 if( fValue < 0.0 )
1608 throw lang::IllegalArgumentException();
1609 return fValue > 0.0;
1613 bool ScaDoubleListGE0::CheckInsert( double fValue ) const
1615 if( fValue < 0.0 )
1616 throw lang::IllegalArgumentException();
1617 return true;
1621 Complex::Complex( const OUString& rStr )
1623 if( !ParseString( rStr, *this ) )
1624 throw lang::IllegalArgumentException();
1628 inline bool Complex::IsImagUnit( sal_Unicode c )
1630 return c == 'i' || c == 'j';
1633 bool Complex::ParseString( const OUString& rStr, Complex& rCompl )
1635 rCompl.c = '\0'; // do not force a symbol, if only real part present
1637 const sal_Unicode* pStr = rStr.getStr();
1639 if( IsImagUnit( *pStr ) && rStr.getLength() == 1)
1641 rCompl.r = 0.0;
1642 rCompl.i = 1.0;
1643 rCompl.c = *pStr;
1644 return true;
1647 double f;
1649 if( !ParseDouble( pStr, f ) )
1650 return false;
1652 switch( *pStr )
1654 case '-': // imag part follows
1655 case '+':
1657 double r = f;
1658 if( IsImagUnit( pStr[ 1 ] ) )
1660 rCompl.c = pStr[ 1 ];
1661 if( pStr[ 2 ] == 0 )
1663 rCompl.r = f;
1664 rCompl.i = ( *pStr == '+' )? 1.0 : -1.0;
1665 return true;
1668 else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) )
1670 rCompl.c = *pStr;
1671 pStr++;
1672 if( *pStr == 0 )
1674 rCompl.r = r;
1675 rCompl.i = f;
1676 return true;
1680 break;
1681 case 'j':
1682 case 'i':
1683 rCompl.c = *pStr;
1684 pStr++;
1685 if( *pStr == 0 )
1687 rCompl.i = f;
1688 rCompl.r = 0.0;
1689 return true;
1691 break;
1692 case 0: // only real-part
1693 rCompl.r = f;
1694 rCompl.i = 0.0;
1695 return true;
1698 return false;
1702 OUString Complex::GetString() const
1704 CHK_FINITE(r);
1705 CHK_FINITE(i);
1706 OUStringBuffer aRet;
1708 bool bHasImag = i != 0.0;
1709 bool bHasReal = !bHasImag || (r != 0.0);
1711 if( bHasReal )
1712 aRet.append(::GetString( r, false ));
1713 if( bHasImag )
1715 if( i == 1.0 )
1717 if( bHasReal )
1718 aRet.append('+');
1720 else if( i == -1.0 )
1721 aRet.append('-');
1722 else
1723 aRet.append(::GetString( i, bHasReal ));
1724 aRet.append((c != 'j') ? 'i' : 'j');
1727 return aRet.makeStringAndClear();
1731 double Complex::Arg() const
1733 if( r == 0.0 && i == 0.0 )
1734 throw lang::IllegalArgumentException();
1736 double phi = acos( r / Abs() );
1738 if( i < 0.0 )
1739 phi = -phi;
1741 return phi;
1745 void Complex::Power( double fPower )
1747 if( r == 0.0 && i == 0.0 )
1749 if( fPower <= 0 )
1750 throw lang::IllegalArgumentException();
1751 r = i = 0.0;
1752 return;
1755 double p, phi;
1757 p = Abs();
1759 phi = acos( r / p );
1760 if( i < 0.0 )
1761 phi = -phi;
1763 p = pow( p, fPower );
1764 phi *= fPower;
1766 r = cos( phi ) * p;
1767 i = sin( phi ) * p;
1771 void Complex::Sqrt()
1773 static const double fMultConst = 0.7071067811865475; // ...2440084436210485 = 1/sqrt(2)
1774 double p = Abs();
1775 double i_ = sqrt( p - r ) * fMultConst;
1777 r = sqrt( p + r ) * fMultConst;
1778 i = ( i < 0.0 )? -i_ : i_;
1782 void Complex::Sin()
1784 if( !::rtl::math::isValidArcArg( r ) )
1785 throw lang::IllegalArgumentException();
1787 if( i )
1789 double r_;
1791 r_ = sin( r ) * cosh( i );
1792 i = cos( r ) * sinh( i );
1793 r = r_;
1795 else
1796 r = sin( r );
1800 void Complex::Cos()
1802 if( !::rtl::math::isValidArcArg( r ) )
1803 throw lang::IllegalArgumentException();
1805 if( i )
1807 double r_;
1809 r_ = cos( r ) * cosh( i );
1810 i = -( sin( r ) * sinh( i ) );
1811 r = r_;
1813 else
1814 r = cos( r );
1818 void Complex::Div( const Complex& z )
1820 if( z.r == 0 && z.i == 0 )
1821 throw lang::IllegalArgumentException();
1823 double a1 = r;
1824 double a2 = z.r;
1825 double b1 = i;
1826 double b2 = z.i;
1828 double f = 1.0 / ( a2 * a2 + b2 * b2 );
1830 r = ( a1 * a2 + b1 * b2 ) * f;
1831 i = ( a2 * b1 - a1 * b2 ) * f;
1833 if( !c ) c = z.c;
1837 void Complex::Exp()
1839 double fE = exp( r );
1840 r = fE * cos( i );
1841 i = fE * sin( i );
1845 void Complex::Ln()
1847 if( r == 0.0 && i == 0.0 )
1848 throw lang::IllegalArgumentException();
1850 double fAbs = Abs();
1851 bool bNegi = i < 0.0;
1853 i = acos( r / fAbs );
1855 if( bNegi )
1856 i = -i;
1858 r = log( fAbs );
1862 void Complex::Log10()
1864 Ln();
1865 Mult( 0.434294481903251828 ); // * log10( e )
1869 void Complex::Log2()
1871 Ln();
1872 Mult( 1.442695040888963407 ); // * log2( e )
1876 void Complex::Tan()
1878 if ( i )
1880 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1881 throw lang::IllegalArgumentException();
1882 double fScale =1.0 / ( cos( 2.0 * r ) + cosh( 2.0 * i ));
1883 r = sin( 2.0 * r ) * fScale;
1884 i = sinh( 2.0 * i ) * fScale;
1886 else
1888 if( !::rtl::math::isValidArcArg( r ) )
1889 throw lang::IllegalArgumentException();
1890 r = tan( r );
1895 void Complex::Sec()
1897 if( i )
1899 if( !::rtl::math::isValidArcArg( 2 * r ) )
1900 throw lang::IllegalArgumentException();
1901 double fScale = 1.0 / (cosh( 2.0 * i) + cos ( 2.0 * r));
1902 double r_;
1903 r_ = 2.0 * cos( r ) * cosh( i ) * fScale;
1904 i = 2.0 * sin( r ) * sinh( i ) * fScale;
1905 r = r_;
1907 else
1909 if( !::rtl::math::isValidArcArg( r ) )
1910 throw lang::IllegalArgumentException();
1911 r = 1.0 / cos( r );
1916 void Complex::Csc()
1918 if( i )
1920 if( !::rtl::math::isValidArcArg( 2 * r ) )
1921 throw lang::IllegalArgumentException();
1922 double fScale = 1.0 / (cosh( 2.0 * i) - cos ( 2.0 * r));
1923 double r_;
1924 r_ = 2.0 * sin( r ) * cosh( i ) * fScale;
1925 i = -2.0 * cos( r ) * sinh( i ) * fScale;
1926 r = r_;
1928 else
1930 if( !::rtl::math::isValidArcArg( r ) )
1931 throw lang::IllegalArgumentException();
1932 r = 1.0 / sin( r );
1937 void Complex::Cot()
1939 if ( i )
1941 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1942 throw lang::IllegalArgumentException();
1943 double fScale =1.0 / ( cosh( 2.0 * i ) - cos( 2.0 * r ) );
1944 r = sin( 2.0 * r ) * fScale;
1945 i = - ( sinh( 2.0 * i ) * fScale );
1947 else
1949 if( !::rtl::math::isValidArcArg( r ) )
1950 throw lang::IllegalArgumentException();
1951 r = 1.0 / tan( r );
1956 void Complex::Sinh()
1958 if( !::rtl::math::isValidArcArg( r ) )
1959 throw lang::IllegalArgumentException();
1961 if( i )
1963 double r_;
1964 r_ = sinh( r ) * cos( i );
1965 i = cosh( r ) * sin( i );
1966 r = r_;
1968 else
1969 r = sinh( r );
1973 void Complex::Cosh()
1975 if( !::rtl::math::isValidArcArg( r ) )
1976 throw lang::IllegalArgumentException();
1978 if( i )
1980 double r_;
1981 r_ = cosh( r ) * cos( i );
1982 i = sinh( r ) * sin( i );
1983 r = r_;
1985 else
1986 r = cosh( r );
1990 void Complex::Sech()
1992 if ( i )
1994 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1995 throw lang::IllegalArgumentException();
1996 double fScale =1.0 / ( cosh( 2.0 * r ) + cos( 2.0 * i ));
1997 double r_;
1998 r_ = 2.0 * cosh( r ) * cos( i ) * fScale;
1999 i = - (2.0 * sinh( r ) * sin( i ) * fScale );
2000 r = r_ ;
2002 else
2004 if( !::rtl::math::isValidArcArg( r ) )
2005 throw lang::IllegalArgumentException();
2006 r = 1.0 / cosh( r );
2011 void Complex::Csch()
2013 if ( i )
2015 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
2016 throw lang::IllegalArgumentException();
2017 double fScale =1.0 / ( cosh( 2.0 * r ) - cos( 2.0 * i ));
2018 double r_;
2019 r_ = 2.0 * sinh( r ) * cos( i ) * fScale;
2020 i = - ( 2.0 * cosh( r ) * sin( i ) * fScale );
2021 r = r_ ;
2023 else
2025 if( !::rtl::math::isValidArcArg( r ) )
2026 throw lang::IllegalArgumentException();
2027 r = 1.0 / sinh( r );
2032 ComplexList::~ComplexList()
2037 void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r, ComplListAppendHandl eAH )
2039 sal_Int32 n1, n2;
2040 sal_Int32 nE1 = r.getLength();
2041 sal_Int32 nE2;
2042 bool bEmpty0 = eAH == AH_EmpyAs0;
2043 bool bErrOnEmpty = eAH == AH_EmptyAsErr;
2045 for( n1 = 0 ; n1 < nE1 ; n1++ )
2047 const uno::Sequence< OUString >& rList = r[ n1 ];
2048 nE2 = rList.getLength();
2050 for( n2 = 0 ; n2 < nE2 ; n2++ )
2052 const OUString& rStr = rList[ n2 ];
2054 if( !rStr.isEmpty() )
2055 Append( Complex( rStr ) );
2056 else if( bEmpty0 )
2057 Append( Complex( 0.0 ) );
2058 else if( bErrOnEmpty )
2059 throw lang::IllegalArgumentException();
2065 void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars, ComplListAppendHandl eAH )
2067 sal_Int32 nEle = aMultPars.getLength();
2068 bool bEmpty0 = eAH == AH_EmpyAs0;
2069 bool bErrOnEmpty = eAH == AH_EmptyAsErr;
2071 for( sal_Int32 i = 0 ; i < nEle ; i++ )
2073 const uno::Any& r = aMultPars[ i ];
2074 switch( r.getValueTypeClass() )
2076 case uno::TypeClass_VOID: break;
2077 case uno::TypeClass_STRING:
2079 auto pStr = o3tl::forceAccess<OUString>(r);
2081 if( !pStr->isEmpty() )
2082 Append( Complex( *pStr ) );
2083 else if( bEmpty0 )
2084 Append( Complex( 0.0 ) );
2085 else if( bErrOnEmpty )
2086 throw lang::IllegalArgumentException();
2088 break;
2089 case uno::TypeClass_DOUBLE:
2090 Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) );
2091 break;
2092 case uno::TypeClass_SEQUENCE:
2094 uno::Sequence< uno::Sequence< uno::Any > > aValArr;
2095 if( !(r >>= aValArr) )
2096 throw lang::IllegalArgumentException();
2098 sal_Int32 nE = aValArr.getLength();
2099 const uno::Sequence< uno::Any >* pArr = aValArr.getConstArray();
2100 for( sal_Int32 n = 0 ; n < nE ; n++ )
2101 Append( pArr[ n ], eAH );
2103 break;
2104 default:
2105 throw lang::IllegalArgumentException();
2110 ConvertData::ConvertData(const sal_Char p[], double fC, ConvertDataClass e, bool bPrefSupport)
2111 : fConst(fC)
2112 , aName(p, strlen(p), RTL_TEXTENCODING_MS_1252)
2113 , eClass(e)
2114 , bPrefixSupport(bPrefSupport)
2118 ConvertData::~ConvertData()
2122 sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const
2124 OUString aStr = rRef;
2125 sal_Int32 nLen = rRef.getLength();
2126 sal_Int32 nIndex = rRef.lastIndexOf( '^' );
2127 if( nIndex > 0 && nIndex == ( nLen - 2 ) )
2128 aStr = aStr.copy( 0, nLen - 2 ) + OUStringLiteral1( aStr[ nLen - 1 ] );
2129 if( aName == aStr )
2130 return 0;
2131 else
2133 const sal_Unicode* p = aStr.getStr();
2135 nLen = aStr.getLength();
2136 bool bPref = bPrefixSupport;
2137 bool bOneChar = (bPref && nLen > 1 && (aName == p + 1));
2138 if (bOneChar || (bPref && nLen > 2 && (aName == p + 2) &&
2139 *p == 'd' && *(p+1) == 'a'))
2141 sal_Int16 n;
2142 switch( *p )
2144 case 'y': n = -24; break; // yocto
2145 case 'z': n = -21; break; // zepto
2146 case 'a': n = -18; break;
2147 case 'f': n = -15; break;
2148 case 'p': n = -12; break;
2149 case 'n': n = -9; break;
2150 case 'u': n = -6; break;
2151 case 'm': n = -3; break;
2152 case 'c': n = -2; break;
2153 case 'd':
2155 if ( bOneChar )
2156 n = -1; // deci
2157 else
2158 n = 1; // deca
2160 break;
2161 case 'e': n = 1; break;
2162 case 'h': n = 2; break;
2163 case 'k': n = 3; break;
2164 case 'M': n = 6; break;
2165 case 'G': n = 9; break;
2166 case 'T': n = 12; break;
2167 case 'P': n = 15; break;
2168 case 'E': n = 18; break;
2169 case 'Z': n = 21; break; // zetta
2170 case 'Y': n = 24; break; // yotta
2171 default:
2172 n = INV_MATCHLEV;
2175 // We could weed some nonsense out, ODFF doesn't say so though.
2176 #if 0
2177 if (n < 0 && Class() == CDC_Information)
2178 n = INV_MATCHLEV; // milli-bits doesn't make sense
2179 #endif
2181 //! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------
2182 if( n != INV_MATCHLEV )
2184 sal_Unicode cLast = p[ aStr.getLength() - 1 ];
2185 if( cLast == '2' )
2186 n *= 2;
2187 else if( cLast == '3' )
2188 n *= 3;
2190 //! </HACK> -------------------------------------------------------------------
2192 return n;
2194 else if ( nLen > 2 && ( aName == p + 2 ) && ( Class() == CDC_Information ) )
2196 const sal_Unicode* pStr = aStr.getStr();
2197 if ( *(pStr + 1) != 'i')
2198 return INV_MATCHLEV;
2199 sal_Int16 n;
2200 switch( *pStr )
2202 case 'k': n = 10; break;
2203 case 'M': n = 20; break;
2204 case 'G': n = 30; break;
2205 case 'T': n = 40; break;
2206 case 'P': n = 50; break;
2207 case 'E': n = 60; break;
2208 case 'Z': n = 70; break;
2209 case 'Y': n = 80; break;
2210 default:
2211 n = INV_MATCHLEV;
2213 return n;
2215 else
2216 return INV_MATCHLEV;
2221 double ConvertData::Convert(
2222 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2224 if( Class() != r.Class() )
2225 throw lang::IllegalArgumentException();
2227 bool bBinFromLev = ( nLevFrom > 0 && ( nLevFrom % 10 ) == 0 );
2228 bool bBinToLev = ( nLevTo > 0 && ( nLevTo % 10 ) == 0 );
2230 if ( Class() == CDC_Information && ( bBinFromLev || bBinToLev ) )
2232 if ( bBinFromLev && bBinToLev )
2234 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo );
2235 f *= r.fConst / fConst;
2236 if( nLevFrom )
2237 f *= pow( 2.0, nLevFrom );
2239 else if ( bBinFromLev )
2240 f *= ( r.fConst / fConst ) * ( pow( 2.0, nLevFrom ) / pow( 10.0, nLevTo ) );
2241 else
2242 f *= ( r.fConst / fConst ) * ( pow( 10.0, nLevFrom ) / pow( 2.0, nLevTo ) );
2243 return f;
2246 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo ); // effective level
2248 f *= r.fConst / fConst;
2250 if( nLevFrom )
2251 f = ::rtl::math::pow10Exp( f, nLevFrom );
2253 return f;
2257 double ConvertData::ConvertToBase( double f, sal_Int16 n ) const
2259 return ::rtl::math::pow10Exp( f / fConst, n );
2263 double ConvertData::ConvertFromBase( double f, sal_Int16 n ) const
2265 return ::rtl::math::pow10Exp( f * fConst, -n );
2268 ConvertDataLinear::~ConvertDataLinear()
2272 double ConvertDataLinear::Convert(
2273 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2275 if( Class() != r.Class() )
2276 throw lang::IllegalArgumentException();
2277 return r.ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo );
2281 double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const
2283 if( n )
2284 f = ::rtl::math::pow10Exp( f, n );
2286 f /= fConst;
2287 f -= fOffs;
2289 return f;
2293 double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const
2295 f += fOffs;
2296 f *= fConst;
2298 if( n )
2299 f = ::rtl::math::pow10Exp( f, -n );
2301 return f;
2305 ConvertDataList::ConvertDataList()
2307 #define NEWD(str,unit,cl) maVector.push_back(new ConvertData(str,unit,cl))
2308 #define NEWDP(str,unit,cl) maVector.push_back(new ConvertData(str,unit,cl,true))
2309 #define NEWL(str,unit,offs,cl) maVector.push_back(new ConvertDataLinear(str,unit,offs,cl))
2310 #define NEWLP(str,unit,offs,cl) maVector.push_back(new ConvertDataLinear(str,unit,offs,cl,true))
2312 // *** are extra and not standard Excel Analysis Addin!
2314 // MASS: 1 Gram is...
2315 NEWDP( "g", 1.0000000000000000E00, CDC_Mass ); // Gram
2316 NEWD( "sg", 6.8522050005347800E-05, CDC_Mass ); // Pieces
2317 NEWD( "lbm", 2.2046229146913400E-03, CDC_Mass ); // Pound (commercial weight)
2318 NEWDP( "u", 6.0221370000000000E23, CDC_Mass ); // U (atomic mass)
2319 NEWD( "ozm", 3.5273971800362700E-02, CDC_Mass ); // Ounce (commercial weight)
2320 NEWD( "stone", 1.574730e-04, CDC_Mass ); // *** Stone
2321 NEWD( "ton", 1.102311e-06, CDC_Mass ); // *** Ton
2322 NEWD( "grain", 1.543236E01, CDC_Mass ); // *** Grain
2323 NEWD( "pweight", 7.054792E-01, CDC_Mass ); // *** Pennyweight
2324 NEWD( "hweight", 1.968413E-05, CDC_Mass ); // *** Hundredweight
2325 NEWD( "shweight", 2.204623E-05, CDC_Mass ); // *** Shorthundredweight
2326 NEWD( "brton", 9.842065E-07, CDC_Mass ); // *** Gross Registered Ton
2327 NEWD( "cwt", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight
2328 NEWD( "shweight", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight also
2329 NEWD( "uk_cwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight
2330 NEWD( "lcwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2331 NEWD( "hweight", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2332 NEWD( "uk_ton", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton
2333 NEWD( "LTON", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton also
2335 // LENGTH: 1 Meter is...
2336 NEWDP( "m", 1.0000000000000000E00, CDC_Length ); // Meter
2337 NEWD( "mi", 6.2137119223733397E-04, CDC_Length ); // Britsh Mile 6,21371192237333969617434184363e-4
2338 NEWD( "Nmi", 5.3995680345572354E-04, CDC_Length ); // Nautical Mile 5,39956803455723542116630669546e-4
2339 NEWD( "in", 3.9370078740157480E01, CDC_Length ); // Inch 39,37007874015748031496062992126
2340 NEWD( "ft", 3.2808398950131234E00, CDC_Length ); // Foot 3,2808398950131233595800524934383
2341 NEWD( "yd", 1.0936132983377078E00, CDC_Length ); // Yard 1,0936132983377077865266841644794
2342 NEWDP( "ang", 1.0000000000000000E10, CDC_Length ); // Angstroem
2343 NEWD( "Pica", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2344 NEWD( "picapt", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2345 NEWD( "pica", 2.36220472441E02, CDC_Length ); // pica (1/6 Inch)
2346 NEWD( "ell", 8.748906E-01, CDC_Length ); // *** Ell
2347 NEWDP( "parsec", 3.240779E-17, CDC_Length ); // *** Parsec
2348 NEWDP( "pc", 3.240779E-17, CDC_Length ); // *** Parsec also
2349 NEWDP( "lightyear", 1.0570234557732930E-16, CDC_Length ); // *** Light Year
2350 NEWDP( "ly", 1.0570234557732930E-16, CDC_Length ); // *** Light Year also
2351 NEWD( "survey_mi", 6.2136994949494949E-04, CDC_Length ); // U.S. survey mile
2353 // TIME: 1 Second is...
2354 NEWD( "yr", 3.1688087814028950E-08, CDC_Time ); // Year
2355 NEWD( "day", 1.1574074074074074E-05, CDC_Time ); // Day
2356 NEWD( "d", 1.1574074074074074E-05, CDC_Time ); // Day also
2357 NEWD( "hr", 2.7777777777777778E-04, CDC_Time ); // Hour
2358 NEWD( "mn", 1.6666666666666667E-02, CDC_Time ); // Minute
2359 NEWD( "min", 1.6666666666666667E-02, CDC_Time ); // Minute also
2360 NEWDP( "sec", 1.0000000000000000E00, CDC_Time ); // Second
2361 NEWDP( "s", 1.0000000000000000E00, CDC_Time ); // Second also
2363 // PRESSURE: 1 Pascal is...
2364 NEWDP( "Pa", 1.0000000000000000E00, CDC_Pressure ); // Pascal
2365 NEWDP( "atm", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere
2366 NEWDP( "at", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere also
2367 NEWDP( "mmHg", 7.5006170799862700E-03, CDC_Pressure ); // mm Hg (Mercury)
2368 NEWD( "Torr", 7.5006380000000000E-03, CDC_Pressure ); // *** Torr
2369 NEWD( "psi", 1.4503770000000000E-04, CDC_Pressure ); // *** Psi
2371 // FORCE: 1 Newton is...
2372 NEWDP( "N", 1.0000000000000000E00, CDC_Force ); // Newton
2373 NEWDP( "dyn", 1.0000000000000000E05, CDC_Force ); // Dyn
2374 NEWDP( "dy", 1.0000000000000000E05, CDC_Force ); // Dyn also
2375 NEWD( "lbf", 2.24808923655339E-01, CDC_Force ); // Pound-Force
2376 NEWDP( "pond", 1.019716E02, CDC_Force ); // *** Pond
2378 // ENERGY: 1 Joule is...
2379 NEWDP( "J", 1.0000000000000000E00, CDC_Energy ); // Joule
2380 NEWDP( "e", 1.0000000000000000E07, CDC_Energy ); // Erg -> https://en.wikipedia.org/wiki/Erg
2381 NEWDP( "c", 2.3900624947346700E-01, CDC_Energy ); // Thermodynamical Calorie
2382 NEWDP( "cal", 2.3884619064201700E-01, CDC_Energy ); // Calorie
2383 NEWDP( "eV", 6.2414570000000000E18, CDC_Energy ); // Electronvolt
2384 NEWDP( "ev", 6.2414570000000000E18, CDC_Energy ); // Electronvolt also
2385 NEWD( "HPh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours
2386 NEWD( "hh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours also
2387 NEWDP( "Wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours
2388 NEWDP( "wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours also
2389 NEWD( "flb", 2.37304222192651E01, CDC_Energy ); // Foot Pound
2390 NEWD( "BTU", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit
2391 NEWD( "btu", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit also
2393 // POWER: 1 Watt is...
2394 NEWDP( "W", 1.0000000000000000E00, CDC_Power ); // Watt
2395 NEWDP( "w", 1.0000000000000000E00, CDC_Power ); // Watt also
2396 NEWD( "HP", 1.341022E-03, CDC_Power ); // Horsepower
2397 NEWD( "h", 1.341022E-03, CDC_Power ); // Horsepower also
2398 NEWD( "PS", 1.359622E-03, CDC_Power ); // *** German Pferdestaerke
2400 // MAGNETISM: 1 Tesla is...
2401 NEWDP( "T", 1.0000000000000000E00, CDC_Magnetism ); // Tesla
2402 NEWDP( "ga", 1.0000000000000000E04, CDC_Magnetism ); // Gauss
2404 // TEMPERATURE: 1 Kelvin is...
2405 NEWL( "C", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius
2406 NEWL( "cel", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius also
2407 NEWL( "F", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit
2408 NEWL( "fah", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit also
2409 NEWLP( "K", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin
2410 NEWLP( "kel", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin also
2411 NEWL( "Reau", 8.0000000000000000E-01, -2.7315000000000000E02, CDC_Temperature ); // *** Reaumur
2412 NEWL( "Rank", 1.8000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // *** Rankine
2414 // VOLUME: 1 Liter is...
2415 NEWD( "tsp", 2.0288413621105798E02, CDC_Volume ); // US teaspoon 1/768 gallon
2416 NEWD( "tbs", 6.7628045403685994E01, CDC_Volume ); // US tablespoon 1/256 gallon
2417 NEWD( "oz", 3.3814022701842997E01, CDC_Volume ); // Ounce Liquid 1/128 gallon
2418 NEWD( "cup", 4.2267528377303746E00, CDC_Volume ); // Cup 1/16 gallon
2419 NEWD( "pt", 2.1133764188651873E00, CDC_Volume ); // US Pint 1/8 gallon
2420 NEWD( "us_pt", 2.1133764188651873E00, CDC_Volume ); // US Pint also
2421 NEWD( "uk_pt", 1.7597539863927023E00, CDC_Volume ); // UK Pint 1/8 imperial gallon
2422 NEWD( "qt", 1.0566882094325937E00, CDC_Volume ); // Quart 1/4 gallon
2423 NEWD( "gal", 2.6417205235814842E-01, CDC_Volume ); // Gallon 1/3.785411784
2424 NEWDP( "l", 1.0000000000000000E00, CDC_Volume ); // Liter
2425 NEWDP( "L", 1.0000000000000000E00, CDC_Volume ); // Liter also
2426 NEWDP( "lt", 1.0000000000000000E00, CDC_Volume ); // Liter also
2427 NEWDP( "m3", 1.0000000000000000E-03, CDC_Volume ); // *** Cubic Meter
2428 NEWD( "mi3", 2.3991275857892772E-13, CDC_Volume ); // *** Cubic Britsh Mile
2429 NEWD( "Nmi3", 1.5742621468581148E-13, CDC_Volume ); // *** Cubic Nautical Mile
2430 NEWD( "in3", 6.1023744094732284E01, CDC_Volume ); // *** Cubic Inch
2431 NEWD( "ft3", 3.5314666721488590E-02, CDC_Volume ); // *** Cubic Foot
2432 NEWD( "yd3", 1.3079506193143922E-03, CDC_Volume ); // *** Cubic Yard
2433 NEWDP( "ang3", 1.0000000000000000E27, CDC_Volume ); // *** Cubic Angstroem
2434 NEWD( "Pica3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2435 NEWD( "picapt3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2436 NEWD( "pica3", 1.31811287245E04, CDC_Volume ); // *** Cubic Pica (1/6 inch)
2437 NEWD( "barrel", 6.2898107704321051E-03, CDC_Volume ); // *** Barrel (=42gal)
2438 NEWD( "bushel", 2.837759E-02, CDC_Volume ); // *** Bushel
2439 NEWD( "regton", 3.531467E-04, CDC_Volume ); // *** Register ton
2440 NEWD( "GRT", 3.531467E-04, CDC_Volume ); // *** Register ton also
2441 NEWD( "Schooner", 2.3529411764705882E00, CDC_Volume ); // *** austr. Schooner
2442 NEWD( "Middy", 3.5087719298245614E00, CDC_Volume ); // *** austr. Middy
2443 NEWD( "Glass", 5.0000000000000000E00, CDC_Volume ); // *** austr. Glass
2444 NEWD( "Sixpack", 0.5, CDC_Volume ); // ***
2445 NEWD( "Humpen", 2.0, CDC_Volume ); // ***
2446 NEWD( "ly3", 1.1810108125623799E-51, CDC_Volume ); // *** Cubic light-year
2447 NEWD( "MTON", 1.4125866688595436E00, CDC_Volume ); // *** Measurement ton
2448 NEWD( "tspm", 2.0000000000000000E02, CDC_Volume ); // *** Modern teaspoon
2449 NEWD( "uk_gal", 2.1996924829908779E-01, CDC_Volume ); // U.K. / Imperial gallon 1/4.54609
2450 NEWD( "uk_qt", 8.7987699319635115E-01, CDC_Volume ); // U.K. / Imperial quart 1/4 imperial gallon
2452 // 1 Square Meter is...
2453 NEWDP( "m2", 1.0000000000000000E00, CDC_Area ); // *** Square Meter
2454 NEWD( "mi2", 3.8610215854244585E-07, CDC_Area ); // *** Square Britsh Mile
2455 NEWD( "Nmi2", 2.9155334959812286E-07, CDC_Area ); // *** Square Nautical Mile
2456 NEWD( "in2", 1.5500031000062000E03, CDC_Area ); // *** Square Inch
2457 NEWD( "ft2", 1.0763910416709722E01, CDC_Area ); // *** Square Foot
2458 NEWD( "yd2", 1.1959900463010803E00, CDC_Area ); // *** Square Yard
2459 NEWDP( "ang2", 1.0000000000000000E20, CDC_Area ); // *** Square Angstroem
2460 NEWD( "Pica2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2461 NEWD( "picapt2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2462 NEWD( "pica2", 5.58001116002232E04, CDC_Area ); // *** Square Pica (1/6 inch)
2463 NEWD( "Morgen", 4.0000000000000000E-04, CDC_Area ); // *** Morgen
2464 NEWDP( "ar", 1.000000E-02, CDC_Area ); // *** Ar
2465 NEWD( "acre", 2.471053815E-04, CDC_Area ); // *** Acre
2466 NEWD( "uk_acre", 2.4710538146716534E-04, CDC_Area ); // *** International acre
2467 NEWD( "us_acre", 2.4710439304662790E-04, CDC_Area ); // *** U.S. survey/statute acre
2468 NEWD( "ly2", 1.1172985860549147E-32, CDC_Area ); // *** Square Light-year
2469 NEWD( "ha", 1.000000E-04, CDC_Area ); // *** Hectare
2471 // SPEED: 1 Meter per Second is...
2472 NEWDP( "m/s", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second
2473 NEWDP( "m/sec", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second also
2474 NEWDP( "m/h", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour
2475 NEWDP( "m/hr", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour also
2476 NEWD( "mph", 2.2369362920544023E00, CDC_Speed ); // *** Britsh Miles per Hour
2477 NEWD( "kn", 1.9438444924406048E00, CDC_Speed ); // *** Knot = Nautical Miles per Hour
2478 NEWD( "admkn", 1.9438446603753486E00, CDC_Speed ); // *** Admiralty Knot
2479 NEWD( "ludicrous speed", 2.0494886343432328E-14, CDC_Speed ); // ***
2480 NEWD( "ridiculous speed", 4.0156958471424288E-06, CDC_Speed); // ***
2482 // INFORMATION: 1 Bit is...
2483 NEWDP( "bit", 1.00E00, CDC_Information); // *** Bit
2484 NEWDP( "byte", 1.25E-01, CDC_Information); // *** Byte
2488 ConvertDataList::~ConvertDataList()
2490 for( std::vector<ConvertData*>::const_iterator it = maVector.begin(); it != maVector.end(); ++it )
2491 delete *it;
2495 double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo )
2497 ConvertData* pFrom = nullptr;
2498 ConvertData* pTo = nullptr;
2499 bool bSearchFrom = true;
2500 bool bSearchTo = true;
2501 sal_Int16 nLevelFrom = 0;
2502 sal_Int16 nLevelTo = 0;
2504 std::vector<ConvertData*>::iterator it = maVector.begin();
2505 while( it != maVector.end() && ( bSearchFrom || bSearchTo ) )
2507 ConvertData* p = *it;
2508 if( bSearchFrom )
2510 sal_Int16 n = p->GetMatchingLevel( rFrom );
2511 if( n != INV_MATCHLEV )
2513 if( n )
2514 { // only first match for partial equality rulz a little bit more
2515 pFrom = p;
2516 nLevelFrom = n;
2518 else
2519 { // ... but exact match rulz most
2520 pFrom = p;
2521 bSearchFrom = false;
2522 nLevelFrom = n;
2527 if( bSearchTo )
2529 sal_Int16 n = p->GetMatchingLevel( rTo );
2530 if( n != INV_MATCHLEV )
2532 if( n )
2533 { // only first match for partial equality rulz a little bit more
2534 pTo = p;
2535 nLevelTo = n;
2537 else
2538 { // ... but exact match rulz most
2539 pTo = p;
2540 bSearchTo = false;
2541 nLevelTo = n;
2546 ++it;
2549 if( !pFrom || !pTo )
2550 throw lang::IllegalArgumentException();
2552 return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo );
2556 ScaDate::ScaDate() :
2557 nOrigDay( 1 ),
2558 nDay( 1 ),
2559 nMonth( 1 ),
2560 nYear( 1900 ),
2561 bLastDayMode( true ),
2562 bLastDay( false ),
2563 b30Days( false ),
2564 bUSMode( false )
2568 ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase )
2570 DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear );
2571 bLastDayMode = (nBase != 5);
2572 bLastDay = (nOrigDay >= ::DaysInMonth( nMonth, nYear ));
2573 b30Days = (nBase == 0) || (nBase == 4);
2574 bUSMode = (nBase == 0);
2575 setDay();
2578 ScaDate::ScaDate( const ScaDate& rCopy ) :
2579 nOrigDay( rCopy.nOrigDay ),
2580 nDay( rCopy.nDay ),
2581 nMonth( rCopy.nMonth ),
2582 nYear( rCopy.nYear ),
2583 bLastDayMode( rCopy.bLastDayMode ),
2584 bLastDay( rCopy.bLastDay ),
2585 b30Days( rCopy.b30Days ),
2586 bUSMode( rCopy.bUSMode )
2590 ScaDate& ScaDate::operator=( const ScaDate& rCopy )
2592 if( this != &rCopy )
2594 nOrigDay = rCopy.nOrigDay;
2595 nDay = rCopy.nDay;
2596 nMonth = rCopy.nMonth;
2597 nYear = rCopy.nYear;
2598 bLastDayMode = rCopy.bLastDayMode;
2599 bLastDay = rCopy.bLastDay;
2600 b30Days = rCopy.b30Days;
2601 bUSMode = rCopy.bUSMode;
2603 return *this;
2606 void ScaDate::setDay()
2608 if( b30Days )
2610 // 30-days-mode: set nDay to 30 if original was last day in month
2611 nDay = std::min( nOrigDay, static_cast< sal_uInt16 >( 30 ) );
2612 if( bLastDay || (nDay >= ::DaysInMonth( nMonth, nYear )) )
2613 nDay = 30;
2615 else
2617 // set nDay to last day in this month if original was last day
2618 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2619 nDay = bLastDay ? nLastDay : std::min( nOrigDay, nLastDay );
2623 sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2625 if( nFrom > nTo )
2626 return 0;
2628 sal_Int32 nRet = 0;
2629 if( b30Days )
2630 nRet = (nTo - nFrom + 1) * 30;
2631 else
2633 for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )
2634 nRet += getDaysInMonth( nMonthIx );
2636 return nRet;
2639 sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2641 if( nFrom > nTo )
2642 return 0;
2644 return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo );
2647 void ScaDate::doAddYears( sal_Int32 nYearCount )
2649 sal_Int32 nNewYear = nYearCount + nYear;
2650 if( (nNewYear < 0) || (nNewYear > 0x7FFF) )
2651 throw lang::IllegalArgumentException();
2652 nYear = static_cast< sal_uInt16 >( nNewYear );
2655 void ScaDate::addMonths( sal_Int32 nMonthCount )
2657 sal_Int32 nNewMonth = nMonthCount + nMonth;
2658 if( nNewMonth > 12 )
2660 --nNewMonth;
2661 doAddYears( nNewMonth / 12 );
2662 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1;
2664 else if( nNewMonth < 1 )
2666 doAddYears( nNewMonth / 12 - 1 );
2667 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 );
2669 else
2670 nMonth = static_cast< sal_uInt16 >( nNewMonth );
2671 setDay();
2674 sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const
2676 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2677 sal_uInt16 nRealDay = (bLastDayMode && bLastDay) ? nLastDay : std::min( nLastDay, nOrigDay );
2678 return ::DateToDays( nRealDay, nMonth, nYear ) - nNullDate;
2681 sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo )
2683 if( rFrom > rTo )
2684 return getDiff( rTo, rFrom );
2686 sal_Int32 nDiff = 0;
2687 ScaDate aFrom( rFrom );
2688 ScaDate aTo( rTo );
2690 if( rTo.b30Days )
2692 // corrections for base 0 (US NASD)
2693 if( rTo.bUSMode )
2695 if( ((rFrom.nMonth == 2) || (rFrom.nDay < 30)) && (aTo.nOrigDay == 31) )
2696 aTo.nDay = 31;
2697 else if( (aTo.nMonth == 2) && aTo.bLastDay )
2698 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2700 // corrections for base 4 (Europe)
2701 else
2703 if( (aFrom.nMonth == 2) && (aFrom.nDay == 30) )
2704 aFrom.nDay = ::DaysInMonth( 2, aFrom.nYear );
2705 if( (aTo.nMonth == 2) && (aTo.nDay == 30) )
2706 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2710 if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) )
2712 // move aFrom to 1st day of next month
2713 nDiff = aFrom.getDaysInMonth() - aFrom.nDay + 1;
2714 aFrom.nOrigDay = aFrom.nDay = 1;
2715 aFrom.bLastDay = false;
2716 aFrom.addMonths( 1 );
2718 if( aFrom.nYear < aTo.nYear )
2720 // move aFrom to 1st day of next year
2721 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 );
2722 aFrom.addMonths( 13 - aFrom.nMonth );
2724 // move aFrom to 1st day of this year
2725 nDiff += aFrom.getDaysInYearRange( aFrom.nYear, aTo.nYear - 1 );
2726 aFrom.addYears( aTo.nYear - aFrom.nYear );
2729 // move aFrom to 1st day of this month
2730 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, aTo.nMonth - 1 );
2731 aFrom.addMonths( aTo.nMonth - aFrom.nMonth );
2733 // finally add remaining days in this month
2734 nDiff += aTo.nDay - aFrom.nDay;
2735 return nDiff > 0 ? nDiff : 0;
2738 bool ScaDate::operator<( const ScaDate& rCmp ) const
2740 if( nYear != rCmp.nYear )
2741 return nYear < rCmp.nYear;
2742 if( nMonth != rCmp.nMonth )
2743 return nMonth < rCmp.nMonth;
2744 if( nDay != rCmp.nDay )
2745 return nDay < rCmp.nDay;
2746 if( bLastDay || rCmp.bLastDay )
2747 return !bLastDay && rCmp.bLastDay;
2748 return nOrigDay < rCmp.nOrigDay;
2752 ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext )
2753 : nDefaultFormat(0)
2754 , bHasValidFormat(false)
2756 xFormatter = util::NumberFormatter::create(xContext);
2759 ScaAnyConverter::~ScaAnyConverter()
2763 void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet )
2765 // try to get default number format
2766 bHasValidFormat = false;
2767 if( xFormatter.is() )
2769 // get XFormatsSupplier from outer XPropertySet
2770 uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY );
2771 if( xFormatsSupp.is() )
2773 // get XNumberFormatTypes from XNumberFormatsSupplier to get standard index
2774 uno::Reference< util::XNumberFormats > xFormats( xFormatsSupp->getNumberFormats() );
2775 uno::Reference< util::XNumberFormatTypes > xFormatTypes( xFormats, uno::UNO_QUERY );
2776 if( xFormatTypes.is() )
2778 lang::Locale eLocale;
2779 nDefaultFormat = xFormatTypes->getStandardIndex( eLocale );
2780 xFormatter->attachNumberFormatsSupplier( xFormatsSupp );
2781 bHasValidFormat = true;
2787 double ScaAnyConverter::convertToDouble( const OUString& rString ) const
2789 double fValue = 0.0;
2790 if( bHasValidFormat )
2794 fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString );
2796 catch( uno::Exception& )
2798 throw lang::IllegalArgumentException();
2801 else
2803 rtl_math_ConversionStatus eStatus;
2804 sal_Int32 nEnd;
2805 fValue = ::rtl::math::stringToDouble( rString, '.', ',', &eStatus, &nEnd );
2806 if( (eStatus != rtl_math_ConversionStatus_Ok) || (nEnd < rString.getLength()) )
2807 throw lang::IllegalArgumentException();
2809 return fValue;
2812 bool ScaAnyConverter::getDouble(
2813 double& rfResult,
2814 const uno::Any& rAny ) const
2816 rfResult = 0.0;
2817 bool bContainsVal = true;
2818 switch( rAny.getValueTypeClass() )
2820 case uno::TypeClass_VOID:
2821 bContainsVal = false;
2822 break;
2823 case uno::TypeClass_DOUBLE:
2824 rAny >>= rfResult;
2825 break;
2826 case uno::TypeClass_STRING:
2828 auto pString = o3tl::forceAccess< OUString >( rAny );
2829 if( !pString->isEmpty() )
2830 rfResult = convertToDouble( *pString );
2831 else
2832 bContainsVal = false;
2834 break;
2835 default:
2836 throw lang::IllegalArgumentException();
2838 return bContainsVal;
2841 bool ScaAnyConverter::getDouble(
2842 double& rfResult,
2843 const uno::Reference< beans::XPropertySet >& xPropSet,
2844 const uno::Any& rAny )
2846 init( xPropSet );
2847 return getDouble( rfResult, rAny );
2850 double ScaAnyConverter::getDouble(
2851 const uno::Reference< beans::XPropertySet >& xPropSet,
2852 const uno::Any& rAny,
2853 double fDefault )
2855 double fResult;
2856 if( !getDouble( fResult, xPropSet, rAny ) )
2857 fResult = fDefault;
2858 return fResult;
2861 bool ScaAnyConverter::getInt32(
2862 sal_Int32& rnResult,
2863 const uno::Reference< beans::XPropertySet >& xPropSet,
2864 const uno::Any& rAny )
2866 double fResult;
2867 bool bContainsVal = getDouble( fResult, xPropSet, rAny );
2868 if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) )
2869 throw lang::IllegalArgumentException();
2871 rnResult = static_cast< sal_Int32 >( fResult );
2872 return bContainsVal;
2875 sal_Int32 ScaAnyConverter::getInt32(
2876 const uno::Reference< beans::XPropertySet >& xPropSet,
2877 const uno::Any& rAny,
2878 sal_Int32 nDefault )
2880 sal_Int32 nResult;
2881 if( !getInt32( nResult, xPropSet, rAny ) )
2882 nResult = nDefault;
2883 return nResult;
2888 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */