Avoid potential negative array index access to cached text.
[LibreOffice.git] / scaddins / source / analysis / analysishelper.cxx
blobdfd41f953d29339278c23281d9401236836a4660
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/Date.hpp>
21 #include <com/sun/star/util/XNumberFormatTypes.hpp>
22 #include <com/sun/star/util/NumberFormatter.hpp>
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>
32 #include "analysisdefs.hxx"
33 #include "analysishelper.hxx"
34 #include <analysis.hrc>
35 #include <strings.hrc>
36 #include "deffuncname.hxx"
38 using namespace ::com::sun::star;
39 using namespace sca::analysis;
41 #define UNIQUE false // function name does not exist in Calc
42 #define DOUBLE true // function name exists in Calc
44 #define STDPAR false // all parameters are described
45 #define INTPAR true // first parameter is internal
47 #define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \
48 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr }
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 }
53 const FuncDataBase pFuncDatas[] =
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 )
159 #undef FUNCDATA
161 namespace sca::analysis {
163 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
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 ];
173 * Convert a date to a count of days starting from 01/01/0001
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.
182 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
184 sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365;
185 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
187 for( sal_uInt16 i = 1; i < nMonth; i++ )
188 nDays += DaysInMonth(i,nYear);
189 nDays += nDay;
191 return nDays;
196 * Convert a count of days starting from 01/01/0001 to a date
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.
205 void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
207 if( nDays < 0 )
208 throw lang::IllegalArgumentException();
210 sal_Int32 nTempDays;
211 sal_Int32 i = 0;
212 bool bCalc;
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 )
223 i++;
224 bCalc = true;
226 else
228 if ( nTempDays > 365 )
230 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
232 i--;
233 bCalc = true;
238 while ( bCalc );
240 rMonth = 1;
241 while ( nTempDays > DaysInMonth( rMonth, rYear ) )
243 nTempDays -= DaysInMonth( rMonth, rYear );
244 rMonth++;
246 rDay = static_cast<sal_uInt16>(nTempDays);
251 * Get the null date used by the spreadsheet document
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
259 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt )
261 if( xOpt.is() )
265 uno::Any aAny = xOpt->getPropertyValue( "NullDate" );
266 util::Date aDate;
267 if( aAny >>= aDate )
268 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
270 catch( uno::Exception& )
275 // no null date available -> no calculations possible
276 throw uno::RuntimeException();
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 )
285 if( nDay1 == 31 )
286 nDay1--;
287 else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) )
288 nDay1 = 30;
290 if( nDay2 == 31 )
292 if( bUSAMethod && nDay1 != 30 )
294 nDay2 = 1;
295 if( nMonth2 == 12 )
297 nYear2++;
298 nMonth2 = 1;
300 else
301 nMonth2++;
303 else
304 nDay2 = 30;
307 return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360;
311 sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod )
313 nDate1 += nNullDate;
314 nDate2 += nNullDate;
316 sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;
318 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
319 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
321 return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod );
325 sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 )
327 sal_uInt16 nLeaps = 0;
328 for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ )
330 if( IsLeapYear( n ) )
331 nLeaps++;
334 sal_uInt32 nSum = 1;
335 nSum += nYear2;
336 nSum -= nYear1;
337 nSum *= 365;
338 nSum += nLeaps;
340 return nSum;
344 sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode,
345 sal_Int32* pOptDaysIn1stYear )
347 bool bNeg = nStartDate > nEndDate;
349 if( bNeg )
350 std::swap( nStartDate, nEndDate );
352 sal_Int32 nRet;
354 switch( nMode )
356 case 0: // 0=USA (NASD) 30/360
357 case 4: // 4=Europe 30/360
359 sal_uInt16 nD1, nM1, nY1, nD2, nM2, nY2;
361 nStartDate += nNullDate;
362 nEndDate += nNullDate;
364 DaysToDate( nStartDate, nD1, nM1, nY1 );
365 DaysToDate( nEndDate, nD2, nM2, nY2 );
367 bool bLeap = IsLeapYear( nY1 );
368 sal_Int32 nDays, nMonths;
370 nMonths = nM2 - nM1;
371 nDays = nD2 - nD1;
373 nMonths += ( nY2 - nY1 ) * 12;
375 nRet = nMonths * 30 + nDays;
376 if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )
377 nRet -= bLeap? 1 : 2;
379 if( pOptDaysIn1stYear )
380 *pOptDaysIn1stYear = 360;
382 break;
383 case 1: // 1=exact/exact
384 if( pOptDaysIn1stYear )
386 sal_uInt16 nD, nM, nY;
388 DaysToDate( nStartDate + nNullDate, nD, nM, nY );
390 *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;
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();
408 return bNeg? -nRet : nRet;
412 double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
414 sal_Int32 nDays1stYear;
415 sal_Int32 nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear );
417 return double( nTotalDays ) / double( nDays1stYear );
421 sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode )
423 switch( nMode )
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
431 sal_uInt16 nD, nM, nY;
432 nDate += nNullDate;
433 DaysToDate( nDate, nD, nM, nY );
434 return IsLeapYear( nY )? 366 : 365;
436 case 3: //3=exact/365
437 return 365;
438 default:
439 throw lang::IllegalArgumentException();
444 // tdf69569 making code compliant with change request for ODFF1.2 par 4.11.7.7
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
451 double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
453 if( nStartDate == nEndDate )
454 return 0.0; // nothing to do...
456 if( nStartDate > nEndDate )
457 std::swap( nStartDate, nEndDate );
459 sal_Int32 nDate1 = nStartDate + nNullDate;
460 sal_Int32 nDate2 = nEndDate + nNullDate;
462 sal_uInt16 nDay1, nDay2;
463 sal_uInt16 nMonth1, nMonth2;
464 sal_uInt16 nYear1, nYear2;
466 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
467 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
469 // calculate days between nDate1 and nDate2
470 sal_Int32 nDayDiff;
471 switch( nMode )
473 case 0: // 0=USA (NASD) 30/360
474 if ( nDay1 == 31 )
476 nDay1--;
478 if ( nDay1 == 30 && nDay2 == 31 )
480 nDay2--;
482 else
484 if ( nMonth1 == 2 && nDay1 == ( IsLeapYear( nYear1 ) ? 29 : 28 ) )
486 nDay1 = 30;
487 if ( nMonth2 == 2 && nDay2 == ( IsLeapYear( nYear2 ) ? 29 : 28 ) )
489 nDay2 = 30;
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 )
503 nDay1--;
505 if ( nDay2 == 31 )
507 nDay2--;
509 nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
510 break;
511 default:
512 throw lang::IllegalArgumentException();
515 //calculate days in year
516 double nDaysInYear;
517 switch( nMode )
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
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 ) ) )
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 );
538 nDaysInYear = static_cast<double>(nDayCount) / static_cast<double>( nYear2 - nYear1 + 1 );
540 else
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 ) ) ) ) );
550 // ODFv1.2 part 2 section 4.11.7.7.8 (CHANGE REQUEST PENDING, see tdf6959)
551 if ( !isYearDifferent && IsLeapYear( nYear1 ) )
553 nDaysInYear = 366;
555 else
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 ) ) ) ) ) )
567 nDaysInYear = 366;
569 else
571 nDaysInYear = 365;
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();
585 return double( nDayDiff ) / nDaysInYear;
588 double BinomialCoefficient( double n, double k )
590 // This method is a copy of BinomKoeff()
591 // found in sc/source/core/tool/interpr3.cxx
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
601 nVal = n/k;
602 n--;
603 k--;
604 while (k > 0.0)
606 nVal *= n/k;
607 k--;
608 n--;
611 return nVal;
614 double GetGcd( double f1, double f2 )
616 double f = fmod( f1, f2 );
617 while( f > 0.0 )
619 f1 = f2;
620 f2 = f;
621 f = fmod( f1, f2 );
624 return f2;
628 double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim )
630 if ( nBase < 2 || nBase > 36 )
631 throw lang::IllegalArgumentException();
633 sal_uInt32 nStrLen = aStr.getLength();
634 if( nStrLen > nCharLim )
635 throw lang::IllegalArgumentException();
636 else if( !nStrLen )
637 return 0.0;
639 double fVal = 0.0;
641 const sal_Unicode* p = aStr.getStr();
643 sal_uInt16 nFirstDig = 0;
644 bool bFirstDig = true;
645 double fBase = nBase;
647 while ( *p )
649 sal_uInt16 n;
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;
660 if( n >= nBase )
661 throw lang::IllegalArgumentException(); // illegal char!
663 if( bFirstDig )
665 bFirstDig = false;
666 nFirstDig = n;
668 fVal = fVal * fBase + double( n );
670 p++;
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;
680 return fVal;
684 static char GetMaxChar( sal_uInt16 nBase )
686 const char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
687 return c[ nBase ];
691 OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase,
692 sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces )
694 fNum = ::rtl::math::approxFloor( fNum );
695 fMin = ::rtl::math::approxFloor( fMin );
696 fMax = ::rtl::math::approxFloor( fMax );
698 if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) )
699 throw lang::IllegalArgumentException();
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;
706 OUString aRet(OUString::number(nNum, nBase).toAsciiUpperCase());
709 if( bUsePlaces )
711 sal_Int32 nLen = aRet.getLength();
712 if( !bNeg && nLen > nPlaces )
714 throw lang::IllegalArgumentException();
716 else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) )
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;
726 return aRet;
729 double Erf( double x )
731 return std::erf(x);
734 double Erfc( double x )
736 return std::erfc(x);
739 static bool IsNum( sal_Unicode c )
741 return c >= '0' && c <= '9';
745 static bool IsComma( sal_Unicode c )
747 return c == '.' || c == ',';
751 static bool IsExpStart( sal_Unicode c )
753 return c == 'e' || c == 'E';
757 static bool IsImagUnit( sal_Unicode c )
759 return c == 'i' || c == 'j';
763 static sal_uInt16 GetVal( sal_Unicode c )
765 return sal_uInt16( c - '0' );
769 bool ParseDouble( const sal_Unicode*& rp, double& rRet )
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
778 enum State { S_End = 0, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp };
780 State eS = S_Sign;
782 bool bNegNum = false;
783 bool bNegExp = false;
785 const sal_Unicode* p = rp;
786 sal_Unicode c;
788 while( eS )
790 c = *p;
791 switch( eS )
793 case S_Sign:
794 if( IsNum( c ) )
796 fInt = GetVal( c );
797 nDigCnt--;
798 eS = S_Int;
800 else if( c == '-' )
802 bNegNum = true;
803 eS = S_IntStart;
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 ) )
815 fInt = GetVal( c );
816 nDigCnt--;
817 eS = S_Int;
819 else if( IsComma( c ) )
820 eS = S_Frac;
821 else if( IsImagUnit( c ) )
823 rRet = 0.0;
824 return true;
826 else
827 return false;
828 break;
829 case S_Int:
830 if( IsNum( c ) )
832 fInt *= 10.0;
833 fInt += double( GetVal( c ) );
834 nDigCnt--;
835 if( !nDigCnt )
836 eS = S_IgnoreIntDigs;
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 ) )
858 fFrac += double( GetVal( c ) ) * fMult;
859 nDigCnt--;
860 if( nDigCnt )
861 fMult *= 0.1;
862 else
863 eS = S_IgnoreFracDigs;
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 ) )
879 nExp = GetVal( c );
880 eS = S_Exp;
882 else if( c == '-' )
884 bNegExp = true;
885 eS = S_Exp;
887 else if( c != '+' )
888 eS = S_End;
889 break;
890 case S_Exp:
891 if( IsNum( c ) )
893 nExp *= 10;
894 nExp += GetVal( c );
895 if( nExp > nMaxExp )
896 return false;
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;
906 p++;
909 p--; // set pointer back to last
910 rp = p;
912 fInt += fFrac;
914 if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error
916 sal_Int32 nLog10 = sal_Int32( log10( fInt ) );
918 if( bNegExp )
919 nExp = -nExp;
921 if( nLog10 + nExp > nMaxExp )
922 return false;
924 fInt = ::rtl::math::pow10Exp( fInt, nExp );
927 if( bNegNum )
928 fInt = -fInt;
930 rRet = fInt;
932 return true;
936 OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig )
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 );
947 OUString aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 );
949 return aRet;
953 double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
954 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
956 sal_uInt32 nPer = sal_uInt32( fPer );
957 double fUsePer = 1.0 / fRate;
958 double fAmorCoeff;
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;
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
974 for( sal_uInt32 n = 0 ; n < nPer ; n++ )
976 fNRate = ::rtl::math::round( fRate * fCost );
977 fRest -= fNRate;
979 if( fRest < 0.0 )
981 switch( nPer - n )
983 case 0:
984 case 1:
985 return ::rtl::math::round( fCost * 0.5 );
986 default:
987 return 0.0;
991 fCost -= fNRate;
994 return fNRate;
998 double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
999 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
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 );
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;
1015 if ( fResult > 0.0 )
1016 return fResult;
1017 else
1018 return 0.0;
1022 double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup,
1023 double fYield, sal_Int32 nFreq, sal_Int32 nBase )
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;
1033 double nDiff = fYearfrac * nFreq - fNumOfCoups;
1035 double t;
1037 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1038 fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff );
1040 fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1042 double p = 0.0;
1043 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1044 p += fCoup / pow( fYield, t + nDiff );
1046 p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1048 fDur /= p;
1049 fDur /= double( nFreq );
1051 return fDur;
1055 double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue,
1056 double fRate, double fPrice, sal_Int32 nBase )
1058 double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );
1059 double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );
1060 double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1062 double y = 1.0 + fIssMat * fRate;
1063 y /= fPrice / 100.0 + fIssSet * fRate;
1064 y--;
1065 y /= fSetMat;
1067 return y;
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*/ )
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();
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 )
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;
1093 for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ )
1095 fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase );
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 )
1105 fYield2 *= 2.0;
1106 fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1108 fYieldN = ( fYield2 - fYield1 ) * 0.5;
1110 else
1112 if( fPrice < fPriceN )
1114 fYield1 = fYieldN;
1115 fPrice1 = fPriceN;
1117 else
1119 fYield2 = fYieldN;
1120 fPrice2 = fPriceN;
1123 fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) );
1127 if( fabs( fPrice - fPriceN ) > fPrice / 100.0 )
1128 throw lang::IllegalArgumentException(); // result not precise enough
1130 return fYieldN;
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 )
1137 double fFreq = nFreq;
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 );
1144 double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) );
1145 fRet -= 100.0 * fRate / fFreq * fA / fE;
1147 double fT1 = 100.0 * fRate / fFreq;
1148 double fT2 = 1.0 + fYield / fFreq;
1150 for( double fK = 0.0 ; fK < fN ; fK++ )
1151 fRet += fT1 / pow( fT2, fK + fDSC_E );
1153 return fRet;
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*/ )
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();
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 )
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;
1176 double p = fRedemp + fDCi * 100.0 * fRate / fFreq;
1177 p /= fDSCi * fYield / fFreq + 1.0;
1178 p -= fAi * 100.0 * fRate / fFreq;
1180 return p;
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 )
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;
1192 double y = fRedemp + fDCi * 100.0 * fRate / fFreq;
1193 y /= fPrice + fAi * 100.0 * fRate / fFreq;
1194 y--;
1195 y *= fFreq / fDSCi;
1197 return y;
1201 double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType )
1203 double fPmt;
1204 if( fRate == 0.0 )
1205 fPmt = ( fPv + fFv ) / fNper;
1206 else
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 );
1215 return -fPmt;
1219 double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType )
1221 double fFv;
1222 if( fRate == 0.0 )
1223 fFv = fPv + fPmt * fNper;
1224 else
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;
1233 return -fFv;
1236 // financial functions COUP***
1238 // COUPPCD: find last coupon date before settlement (can be equal to settlement)
1239 /// @throws css::lang::IllegalArgumentException
1240 static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
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 );
1250 double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1252 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1253 throw lang::IllegalArgumentException();
1255 ScaDate aDate;
1256 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1257 return aDate.getDate( nNullDate );
1260 // COUPNCD: find first coupon date after settlement (is never equal to settlement)
1261 /// @throws css::lang::IllegalArgumentException
1262 static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
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 );
1272 double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1274 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1275 throw lang::IllegalArgumentException();
1277 ScaDate aDate;
1278 lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1279 return aDate.getDate( nNullDate );
1282 // COUPDAYBS: get day count: coupon date before settlement <-> settlement
1283 double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1285 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1286 throw lang::IllegalArgumentException();
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 );
1294 // COUPDAYSNC: get day count: settlement <-> coupon date after settlement
1295 double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1297 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1298 throw lang::IllegalArgumentException();
1300 if( (nBase != 0) && (nBase != 4) )
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 );
1307 return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1310 // COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement
1311 double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1313 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1314 throw lang::IllegalArgumentException();
1316 if( nBase == 1 )
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 );
1324 return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq;
1327 // COUPNUM: get count of coupon dates
1328 double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1330 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1331 throw lang::IllegalArgumentException();
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 );
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 )
1349 if (r.pSuffix)
1350 aSuffix = OUString::createFromAscii(r.pSuffix);
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);
1357 sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const
1359 if( !bWithOpt )
1360 nParamNum++;
1362 if( nParamNum > nParam )
1363 return nParam * 2;
1364 else
1365 return nParamNum * 2;
1368 void InitFuncDataList(FuncDataList& rList)
1370 for(const auto & rFuncData : pFuncDatas)
1371 rList.push_back(FuncData(rFuncData));
1374 SortedIndividualInt32List::SortedIndividualInt32List()
1379 SortedIndividualInt32List::~SortedIndividualInt32List()
1384 void SortedIndividualInt32List::Insert( sal_Int32 nDay )
1386 sal_uInt32 nIndex = Count();
1387 while( nIndex )
1389 nIndex--;
1390 sal_Int32 nRef = Get( nIndex );
1391 if( nDay == nRef )
1392 return;
1393 else if( nDay > nRef )
1395 maVector.insert( maVector.begin() + nIndex + 1, nDay );
1396 return;
1399 maVector.insert( maVector.begin(), nDay );
1403 void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1405 if( !nDay )
1406 return;
1408 nDay += nNullDate;
1409 if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) )
1410 Insert( nDay );
1414 void SortedIndividualInt32List::Insert(
1415 double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1417 if( (fDay < -2147483648.0) || (fDay > 2147483649.0) )
1418 throw lang::IllegalArgumentException();
1419 Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend );
1423 bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const
1425 sal_uInt32 nE = Count();
1427 if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) )
1428 return false;
1430 // linear search
1432 for( sal_uInt32 n = 0 ; n < nE ; n++ )
1434 sal_Int32 nRef = Get( n );
1436 if( nRef == nVal )
1437 return true;
1438 else if( nRef > nVal )
1439 return false;
1441 return false;
1445 void SortedIndividualInt32List::InsertHolidayList(
1446 const ScaAnyConverter& rAnyConv,
1447 const uno::Any& rHolAny,
1448 sal_Int32 nNullDate,
1449 bool bInsertOnWeekend )
1451 double fDay;
1452 if( rAnyConv.getDouble( fDay, rHolAny ) )
1453 Insert( fDay, nNullDate, bInsertOnWeekend );
1457 void SortedIndividualInt32List::InsertHolidayList(
1458 ScaAnyConverter& rAnyConv,
1459 const uno::Reference< beans::XPropertySet >& xOptions,
1460 const uno::Any& rHolAny,
1461 sal_Int32 nNullDate )
1463 rAnyConv.init( xOptions );
1464 if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE )
1466 uno::Sequence< uno::Sequence< uno::Any > > aAnySeq;
1467 if( !(rHolAny >>= aAnySeq) )
1468 throw lang::IllegalArgumentException();
1470 for( const uno::Sequence< uno::Any >& rSubSeq : std::as_const(aAnySeq) )
1472 for( const uno::Any& rAny : rSubSeq )
1473 InsertHolidayList( rAnyConv, rAny, nNullDate, false/*bInsertOnWeekend*/ );
1476 else
1477 InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ );
1481 void ScaDoubleList::Append(
1482 const uno::Sequence< uno::Sequence< double > >& rValueSeq )
1484 for( const uno::Sequence< double >& rSubSeq : rValueSeq )
1486 for( const double fValue : rSubSeq )
1487 Append( fValue );
1492 void ScaDoubleList::Append(
1493 const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq )
1495 for( const uno::Sequence< sal_Int32 >& rSubSeq : rValueSeq )
1497 for( const sal_Int32 nValue : rSubSeq )
1498 Append( nValue );
1502 void ScaDoubleList::Append(
1503 const ScaAnyConverter& rAnyConv,
1504 const uno::Any& rAny,
1505 bool bIgnoreEmpty )
1507 if( auto s = o3tl::tryAccess<
1508 css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) )
1509 Append( rAnyConv, *s, bIgnoreEmpty );
1510 else
1512 double fValue;
1513 if( rAnyConv.getDouble( fValue, rAny ) )
1514 Append( fValue );
1515 else if( !bIgnoreEmpty )
1516 Append( 0.0 );
1521 void ScaDoubleList::Append(
1522 const ScaAnyConverter& rAnyConv,
1523 const uno::Sequence< uno::Any >& rAnySeq,
1524 bool bIgnoreEmpty )
1526 for( const uno::Any& rAny : rAnySeq )
1527 Append( rAnyConv, rAny, bIgnoreEmpty );
1531 void ScaDoubleList::Append(
1532 const ScaAnyConverter& rAnyConv,
1533 const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq,
1534 bool bIgnoreEmpty )
1536 for( const uno::Sequence< uno::Any >& rArray : rAnySeq )
1537 Append( rAnyConv, rArray, bIgnoreEmpty );
1540 void ScaDoubleList::Append(
1541 ScaAnyConverter& rAnyConv,
1542 const uno::Reference< beans::XPropertySet >& xOpt,
1543 const uno::Sequence< uno::Any >& rAnySeq )
1545 rAnyConv.init( xOpt );
1546 Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ );
1550 bool ScaDoubleList::CheckInsert( double ) const
1552 return true;
1556 bool ScaDoubleListGT0::CheckInsert( double fValue ) const
1558 if( fValue < 0.0 )
1559 throw lang::IllegalArgumentException();
1560 return fValue > 0.0;
1564 bool ScaDoubleListGE0::CheckInsert( double fValue ) const
1566 if( fValue < 0.0 )
1567 throw lang::IllegalArgumentException();
1568 return true;
1572 Complex::Complex( const OUString& rStr )
1574 if( !ParseString( rStr, *this ) )
1575 throw lang::IllegalArgumentException();
1579 inline bool Complex::IsImagUnit( sal_Unicode c )
1581 return c == 'i' || c == 'j';
1584 bool Complex::ParseString( const OUString& rStr, Complex& rCompl )
1586 rCompl.c = '\0'; // do not force a symbol, if only real part present
1588 const sal_Unicode* pStr = rStr.getStr();
1590 if( IsImagUnit( *pStr ) && rStr.getLength() == 1)
1592 rCompl.r = 0.0;
1593 rCompl.i = 1.0;
1594 rCompl.c = *pStr;
1595 return true;
1598 double f;
1600 if( !ParseDouble( pStr, f ) )
1601 return false;
1603 switch( *pStr )
1605 case '-': // imag part follows
1606 case '+':
1608 double r = f;
1609 if( IsImagUnit( pStr[ 1 ] ) )
1611 rCompl.c = pStr[ 1 ];
1612 if( pStr[ 2 ] == 0 )
1614 rCompl.r = f;
1615 rCompl.i = ( *pStr == '+' )? 1.0 : -1.0;
1616 return true;
1619 else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) )
1621 rCompl.c = *pStr;
1622 pStr++;
1623 if( *pStr == 0 )
1625 rCompl.r = r;
1626 rCompl.i = f;
1627 return true;
1631 break;
1632 case 'j':
1633 case 'i':
1634 rCompl.c = *pStr;
1635 pStr++;
1636 if( *pStr == 0 )
1638 rCompl.i = f;
1639 rCompl.r = 0.0;
1640 return true;
1642 break;
1643 case 0: // only real-part
1644 rCompl.r = f;
1645 rCompl.i = 0.0;
1646 return true;
1649 return false;
1653 OUString Complex::GetString() const
1655 finiteOrThrow(r);
1656 finiteOrThrow(i);
1657 OUStringBuffer aRet;
1659 bool bHasImag = i != 0.0;
1660 bool bHasReal = !bHasImag || (r != 0.0);
1662 if( bHasReal )
1663 aRet.append(::GetString( r, false ));
1664 if( bHasImag )
1666 if( i == 1.0 )
1668 if( bHasReal )
1669 aRet.append('+');
1671 else if( i == -1.0 )
1672 aRet.append('-');
1673 else
1674 aRet.append(::GetString( i, bHasReal ));
1675 aRet.append((c != 'j') ? 'i' : 'j');
1678 return aRet.makeStringAndClear();
1682 double Complex::Arg() const
1684 if( r == 0.0 && i == 0.0 )
1685 throw lang::IllegalArgumentException();
1687 double phi = acos( r / Abs() );
1689 if( i < 0.0 )
1690 phi = -phi;
1692 return phi;
1696 void Complex::Power( double fPower )
1698 if( r == 0.0 && i == 0.0 )
1700 if( fPower <= 0 )
1701 throw lang::IllegalArgumentException();
1702 r = i = 0.0;
1703 return;
1706 double p, phi;
1708 p = Abs();
1710 phi = acos( r / p );
1711 if( i < 0.0 )
1712 phi = -phi;
1714 p = pow( p, fPower );
1715 phi *= fPower;
1717 r = cos( phi ) * p;
1718 i = sin( phi ) * p;
1722 void Complex::Sqrt()
1724 static const double fMultConst = M_SQRT1_2;
1725 double p = Abs();
1726 double i_ = sqrt( p - r ) * fMultConst;
1728 r = sqrt( p + r ) * fMultConst;
1729 i = ( i < 0.0 )? -i_ : i_;
1733 void Complex::Sin()
1735 if( !::rtl::math::isValidArcArg( r ) )
1736 throw lang::IllegalArgumentException();
1738 if( i )
1740 double r_;
1742 r_ = sin( r ) * cosh( i );
1743 i = cos( r ) * sinh( i );
1744 r = r_;
1746 else
1747 r = sin( r );
1751 void Complex::Cos()
1753 if( !::rtl::math::isValidArcArg( r ) )
1754 throw lang::IllegalArgumentException();
1756 if( i )
1758 double r_;
1760 r_ = cos( r ) * cosh( i );
1761 i = -( sin( r ) * sinh( i ) );
1762 r = r_;
1764 else
1765 r = cos( r );
1769 void Complex::Div( const Complex& z )
1771 if( z.r == 0 && z.i == 0 )
1772 throw lang::IllegalArgumentException();
1774 double a1 = r;
1775 double a2 = z.r;
1776 double b1 = i;
1777 double b2 = z.i;
1779 double f = 1.0 / ( a2 * a2 + b2 * b2 );
1781 r = ( a1 * a2 + b1 * b2 ) * f;
1782 i = ( a2 * b1 - a1 * b2 ) * f;
1784 if( !c ) c = z.c;
1788 void Complex::Exp()
1790 double fE = exp( r );
1791 r = fE * cos( i );
1792 i = fE * sin( i );
1796 void Complex::Ln()
1798 if( r == 0.0 && i == 0.0 )
1799 throw lang::IllegalArgumentException();
1801 double fAbs = Abs();
1802 bool bNegi = i < 0.0;
1804 i = acos( r / fAbs );
1806 if( bNegi )
1807 i = -i;
1809 r = log( fAbs );
1813 void Complex::Log10()
1815 Ln();
1816 Mult( M_LOG10E );
1820 void Complex::Log2()
1822 Ln();
1823 Mult( M_LOG2E );
1827 void Complex::Tan()
1829 if ( i )
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;
1837 else
1839 if( !::rtl::math::isValidArcArg( r ) )
1840 throw lang::IllegalArgumentException();
1841 r = tan( r );
1846 void Complex::Sec()
1848 if( i )
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_;
1858 else
1860 if( !::rtl::math::isValidArcArg( r ) )
1861 throw lang::IllegalArgumentException();
1862 r = 1.0 / cos( r );
1867 void Complex::Csc()
1869 if( i )
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_;
1879 else
1881 if( !::rtl::math::isValidArcArg( r ) )
1882 throw lang::IllegalArgumentException();
1883 r = 1.0 / sin( r );
1888 void Complex::Cot()
1890 if ( i )
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 );
1898 else
1900 if( !::rtl::math::isValidArcArg( r ) )
1901 throw lang::IllegalArgumentException();
1902 r = 1.0 / tan( r );
1907 void Complex::Sinh()
1909 if( !::rtl::math::isValidArcArg( r ) )
1910 throw lang::IllegalArgumentException();
1912 if( i )
1914 double r_;
1915 r_ = sinh( r ) * cos( i );
1916 i = cosh( r ) * sin( i );
1917 r = r_;
1919 else
1920 r = sinh( r );
1924 void Complex::Cosh()
1926 if( !::rtl::math::isValidArcArg( r ) )
1927 throw lang::IllegalArgumentException();
1929 if( i )
1931 double r_;
1932 r_ = cosh( r ) * cos( i );
1933 i = sinh( r ) * sin( i );
1934 r = r_;
1936 else
1937 r = cosh( r );
1941 void Complex::Sech()
1943 if ( i )
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_ ;
1953 else
1955 if( !::rtl::math::isValidArcArg( r ) )
1956 throw lang::IllegalArgumentException();
1957 r = 1.0 / cosh( r );
1962 void Complex::Csch()
1964 if ( i )
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_ ;
1974 else
1976 if( !::rtl::math::isValidArcArg( r ) )
1977 throw lang::IllegalArgumentException();
1978 r = 1.0 / sinh( r );
1983 ComplexList::~ComplexList()
1988 void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r )
1990 for( const uno::Sequence< OUString >& rList : r )
1992 for( const OUString& rStr : rList )
1994 if( !rStr.isEmpty() )
1995 Append( Complex( rStr ) );
2001 void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars )
2003 for( const uno::Any& r : aMultPars )
2005 switch( r.getValueTypeClass() )
2007 case uno::TypeClass_VOID: break;
2008 case uno::TypeClass_STRING:
2010 auto pStr = o3tl::forceAccess<OUString>(r);
2012 if( !pStr->isEmpty() )
2013 Append( Complex( *pStr ) );
2015 break;
2016 case uno::TypeClass_DOUBLE:
2017 Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) );
2018 break;
2019 case uno::TypeClass_SEQUENCE:
2021 uno::Sequence< uno::Sequence< uno::Any > > aValArr;
2022 if( !(r >>= aValArr) )
2023 throw lang::IllegalArgumentException();
2025 for( const uno::Sequence< uno::Any >& rArr : std::as_const(aValArr) )
2026 Append( rArr );
2028 break;
2029 default:
2030 throw lang::IllegalArgumentException();
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)
2043 ConvertData::~ConvertData()
2047 sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const
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
2058 const sal_Unicode* p = aStr.getStr();
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'))
2066 sal_Int16 n;
2067 switch( *p )
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':
2080 if ( bOneChar )
2081 n = -1; // deci
2082 else
2083 n = 1; // deca
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;
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
2106 //! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------
2107 if( n != INV_MATCHLEV )
2109 sal_Unicode cLast = p[ aStr.getLength() - 1 ];
2110 if( cLast == '2' )
2111 n *= 2;
2112 else if( cLast == '3' )
2113 n *= 3;
2115 //! </HACK> -------------------------------------------------------------------
2117 return n;
2119 else if ( nLen > 2 && ( aName == p + 2 ) && ( Class() == CDC_Information ) )
2121 const sal_Unicode* pStr = aStr.getStr();
2122 if ( *(pStr + 1) != 'i')
2123 return INV_MATCHLEV;
2124 sal_Int16 n;
2125 switch( *pStr )
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;
2138 return n;
2140 else
2141 return INV_MATCHLEV;
2146 double ConvertData::Convert(
2147 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2149 if( Class() != r.Class() )
2150 throw lang::IllegalArgumentException();
2152 bool bBinFromLev = ( nLevFrom > 0 && ( nLevFrom % 10 ) == 0 );
2153 bool bBinToLev = ( nLevTo > 0 && ( nLevTo % 10 ) == 0 );
2155 if ( Class() == CDC_Information && ( bBinFromLev || bBinToLev ) )
2157 if ( bBinFromLev && bBinToLev )
2159 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo );
2160 f *= r.fConst / fConst;
2161 if( nLevFrom )
2162 f *= pow( 2.0, nLevFrom );
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;
2171 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo ); // effective level
2173 f *= r.fConst / fConst;
2175 if( nLevFrom )
2176 f = ::rtl::math::pow10Exp( f, nLevFrom );
2178 return f;
2182 double ConvertData::ConvertFromBase( double f, sal_Int16 n ) const
2184 return ::rtl::math::pow10Exp( f * fConst, -n );
2187 ConvertDataLinear::~ConvertDataLinear()
2191 double ConvertDataLinear::Convert(
2192 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2194 if( Class() != r.Class() )
2195 throw lang::IllegalArgumentException();
2196 return r.ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo );
2200 double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const
2202 if( n )
2203 f = ::rtl::math::pow10Exp( f, n );
2205 f /= fConst;
2206 f -= fOffs;
2208 return f;
2212 double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const
2214 f += fOffs;
2215 f *= fConst;
2217 if( n )
2218 f = ::rtl::math::pow10Exp( f, -n );
2220 return f;
2224 ConvertDataList::ConvertDataList()
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))
2231 // *** are extra and not standard Excel Analysis Addin!
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
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
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
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
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
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
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
2319 // MAGNETISM: 1 Tesla is...
2320 NEWDP( "T", 1.0000000000000000E00, CDC_Magnetism ); // Tesla
2321 NEWDP( "ga", 1.0000000000000000E04, CDC_Magnetism ); // Gauss
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
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
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
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); // ***
2401 // INFORMATION: 1 Bit is...
2402 NEWDP( "bit", 1.00E00, CDC_Information); // *** Bit
2403 NEWDP( "byte", 1.25E-01, CDC_Information); // *** Byte
2407 ConvertDataList::~ConvertDataList()
2412 double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo )
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;
2421 for( const auto& rItem : maVector )
2423 ConvertData* p = rItem.get();
2424 if( bSearchFrom )
2426 sal_Int16 n = p->GetMatchingLevel( rFrom );
2427 if( n != INV_MATCHLEV )
2429 if( n )
2430 { // only first match for partial equality rulz a little bit more
2431 pFrom = p;
2432 nLevelFrom = n;
2434 else
2435 { // ... but exact match rulz most
2436 pFrom = p;
2437 bSearchFrom = false;
2438 nLevelFrom = n;
2443 if( bSearchTo )
2445 sal_Int16 n = p->GetMatchingLevel( rTo );
2446 if( n != INV_MATCHLEV )
2448 if( n )
2449 { // only first match for partial equality rulz a little bit more
2450 pTo = p;
2451 nLevelTo = n;
2453 else
2454 { // ... but exact match rulz most
2455 pTo = p;
2456 bSearchTo = false;
2457 nLevelTo = n;
2462 if( !bSearchFrom && !bSearchTo )
2463 break;
2466 if( !pFrom || !pTo )
2467 throw lang::IllegalArgumentException();
2469 return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo );
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 )
2485 ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase )
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();
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 )
2507 ScaDate& ScaDate::operator=( const ScaDate& rCopy )
2509 if( this != &rCopy )
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;
2520 return *this;
2523 void ScaDate::setDay()
2525 if( b30Days )
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;
2532 else
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 );
2540 sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2542 if( nFrom > nTo )
2543 return 0;
2545 sal_Int32 nRet = 0;
2546 if( b30Days )
2547 nRet = (nTo - nFrom + 1) * 30;
2548 else
2550 for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )
2551 nRet += getDaysInMonth( nMonthIx );
2553 return nRet;
2556 sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2558 if( nFrom > nTo )
2559 return 0;
2561 return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo );
2564 void ScaDate::doAddYears( sal_Int32 nYearCount )
2566 sal_Int32 nNewYear = nYearCount + nYear;
2567 if( (nNewYear < 0) || (nNewYear > 0x7FFF) )
2568 throw lang::IllegalArgumentException();
2569 nYear = static_cast< sal_uInt16 >( nNewYear );
2572 void ScaDate::addMonths( sal_Int32 nMonthCount )
2574 sal_Int32 nNewMonth = nMonthCount + nMonth;
2575 if( nNewMonth > 12 )
2577 --nNewMonth;
2578 doAddYears( nNewMonth / 12 );
2579 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1;
2581 else if( nNewMonth < 1 )
2583 doAddYears( nNewMonth / 12 - 1 );
2584 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 );
2586 else
2587 nMonth = static_cast< sal_uInt16 >( nNewMonth );
2588 setDay();
2591 sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const
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;
2598 sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo )
2600 if( rFrom > rTo )
2601 return getDiff( rTo, rFrom );
2603 sal_Int32 nDiff = 0;
2604 ScaDate aFrom( rFrom );
2605 ScaDate aTo( rTo );
2607 if( rTo.b30Days )
2609 // corrections for base 0 (US NASD)
2610 if( rTo.bUSMode )
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 );
2617 // corrections for base 4 (Europe)
2618 else
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 );
2627 if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) )
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 );
2635 if( aFrom.nYear < aTo.nYear )
2637 // move aFrom to 1st day of next year
2638 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 );
2639 aFrom.addMonths( 13 - aFrom.nMonth );
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 );
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 );
2650 // finally add remaining days in this month
2651 nDiff += aTo.nDay - aFrom.nDay;
2652 return std::max<sal_Int32>(nDiff, 0);
2655 bool ScaDate::operator<( const ScaDate& rCmp ) const
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;
2669 ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext )
2670 : nDefaultFormat(0)
2671 , bHasValidFormat(false)
2673 xFormatter = util::NumberFormatter::create(xContext);
2676 ScaAnyConverter::~ScaAnyConverter()
2680 void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet )
2682 // try to get default number format
2683 bHasValidFormat = false;
2684 if( !xFormatter.is() )
2685 return;
2687 // get XFormatsSupplier from outer XPropertySet
2688 uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY );
2689 if( !xFormatsSupp.is() )
2690 return;
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() )
2697 lang::Locale eLocale;
2698 nDefaultFormat = xFormatTypes->getStandardIndex( eLocale );
2699 xFormatter->attachNumberFormatsSupplier( xFormatsSupp );
2700 bHasValidFormat = true;
2704 double ScaAnyConverter::convertToDouble( const OUString& rString ) const
2706 double fValue = 0.0;
2707 if( bHasValidFormat )
2711 fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString );
2713 catch( uno::Exception& )
2715 throw lang::IllegalArgumentException();
2718 else
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();
2726 return fValue;
2729 bool ScaAnyConverter::getDouble(
2730 double& rfResult,
2731 const uno::Any& rAny ) const
2733 rfResult = 0.0;
2734 bool bContainsVal = true;
2735 switch( rAny.getValueTypeClass() )
2737 case uno::TypeClass_VOID:
2738 bContainsVal = false;
2739 break;
2740 case uno::TypeClass_DOUBLE:
2741 rAny >>= rfResult;
2742 break;
2743 case uno::TypeClass_STRING:
2745 auto pString = o3tl::forceAccess< OUString >( rAny );
2746 if( !pString->isEmpty() )
2747 rfResult = convertToDouble( *pString );
2748 else
2749 bContainsVal = false;
2751 break;
2752 default:
2753 throw lang::IllegalArgumentException();
2755 return bContainsVal;
2758 bool ScaAnyConverter::getDouble(
2759 double& rfResult,
2760 const uno::Reference< beans::XPropertySet >& xPropSet,
2761 const uno::Any& rAny )
2763 init( xPropSet );
2764 return getDouble( rfResult, rAny );
2767 double ScaAnyConverter::getDouble(
2768 const uno::Reference< beans::XPropertySet >& xPropSet,
2769 const uno::Any& rAny,
2770 double fDefault )
2772 double fResult;
2773 if( !getDouble( fResult, xPropSet, rAny ) )
2774 fResult = fDefault;
2775 return fResult;
2778 bool ScaAnyConverter::getInt32(
2779 sal_Int32& rnResult,
2780 const uno::Reference< beans::XPropertySet >& xPropSet,
2781 const uno::Any& rAny )
2783 double fResult;
2784 bool bContainsVal = getDouble( fResult, xPropSet, rAny );
2785 if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) )
2786 throw lang::IllegalArgumentException();
2788 rnResult = static_cast< sal_Int32 >( fResult );
2789 return bContainsVal;
2792 sal_Int32 ScaAnyConverter::getInt32(
2793 const uno::Reference< beans::XPropertySet >& xPropSet,
2794 const uno::Any& rAny,
2795 sal_Int32 nDefault )
2797 sal_Int32 nResult;
2798 if( !getInt32( nResult, xPropSet, rAny ) )
2799 nResult = nDefault;
2800 return nResult;
2805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */