bump product version to 6.3.0.0.beta1
[LibreOffice.git] / scaddins / source / analysis / analysishelper.cxx
blobc44c55f763dba3709f8b9b0a3c97c0a2e225c4e4
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 <memory>
30 #include "analysisdefs.hxx"
31 #include "analysishelper.hxx"
32 #include <analysis.hrc>
33 #include <strings.hrc>
34 #include "deffuncname.hxx"
36 using namespace ::com::sun::star;
37 using namespace sca::analysis;
39 #define UNIQUE false // function name does not exist in Calc
40 #define DOUBLE true // function name exists in Calc
42 #define STDPAR false // all parameters are described
43 #define INTPAR true // first parameter is internal
45 #define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \
46 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr }
48 #define FUNCDATAS( FUNCNAME, DBL, OPT, NUMOFPAR, CAT, SUFFIX ) \
49 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, SUFFIX }
51 const FuncDataBase pFuncDatas[] =
53 // UNIQUE or INTPAR or
54 // function name DOUBLE STDPAR # of param category
55 FUNCDATA( Workday, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
56 FUNCDATA( Yearfrac, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
57 FUNCDATA( Edate, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
58 FUNCDATAS( Weeknum, DOUBLE, INTPAR, 2, FDCategory::DateTime, "_EXCEL2003" ),
59 FUNCDATA( Eomonth, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
60 FUNCDATAS( Networkdays, DOUBLE, INTPAR, 3, FDCategory::DateTime, "_EXCEL2003" ),
61 FUNCDATA( Iseven, DOUBLE, STDPAR, 1, FDCategory::Inf ),
62 FUNCDATA( Isodd, DOUBLE, STDPAR, 1, FDCategory::Inf ),
63 FUNCDATA( Multinomial, UNIQUE, STDPAR, 1, FDCategory::Math ),
64 FUNCDATA( Seriessum, UNIQUE, STDPAR, 4, FDCategory::Math ),
65 FUNCDATA( Quotient, UNIQUE, STDPAR, 2, FDCategory::Math ),
66 FUNCDATA( Mround, UNIQUE, STDPAR, 2, FDCategory::Math ),
67 FUNCDATA( Sqrtpi, UNIQUE, STDPAR, 1, FDCategory::Math ),
68 FUNCDATA( Randbetween, UNIQUE, STDPAR, 2, FDCategory::Math ),
69 FUNCDATAS( Gcd, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
70 FUNCDATAS( Lcm, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
71 FUNCDATA( Besseli, UNIQUE, STDPAR, 2, FDCategory::Tech ),
72 FUNCDATA( Besselj, UNIQUE, STDPAR, 2, FDCategory::Tech ),
73 FUNCDATA( Besselk, UNIQUE, STDPAR, 2, FDCategory::Tech ),
74 FUNCDATA( Bessely, UNIQUE, STDPAR, 2, FDCategory::Tech ),
75 FUNCDATA( Bin2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
76 FUNCDATA( Bin2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
77 FUNCDATA( Bin2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
78 FUNCDATA( Oct2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
79 FUNCDATA( Oct2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
80 FUNCDATA( Oct2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
81 FUNCDATA( Dec2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
82 FUNCDATA( Dec2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
83 FUNCDATA( Dec2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
84 FUNCDATA( Hex2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
85 FUNCDATA( Hex2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
86 FUNCDATA( Hex2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
87 FUNCDATA( Delta, UNIQUE, INTPAR, 2, FDCategory::Tech ),
88 FUNCDATA( Erf, UNIQUE, INTPAR, 2, FDCategory::Tech ),
89 FUNCDATA( Erfc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
90 FUNCDATA( Gestep, UNIQUE, INTPAR, 2, FDCategory::Tech ),
91 FUNCDATA( Factdouble, UNIQUE, STDPAR, 1, FDCategory::Tech ),
92 FUNCDATA( Imabs, UNIQUE, STDPAR, 1, FDCategory::Tech ),
93 FUNCDATA( Imaginary, UNIQUE, STDPAR, 1, FDCategory::Tech ),
94 FUNCDATA( Impower, UNIQUE, STDPAR, 2, FDCategory::Tech ),
95 FUNCDATA( Imargument, UNIQUE, STDPAR, 1, FDCategory::Tech ),
96 FUNCDATA( Imcos, UNIQUE, STDPAR, 1, FDCategory::Tech ),
97 FUNCDATA( Imdiv, UNIQUE, STDPAR, 2, FDCategory::Tech ),
98 FUNCDATA( Imexp, UNIQUE, STDPAR, 1, FDCategory::Tech ),
99 FUNCDATA( Imconjugate, UNIQUE, STDPAR, 1, FDCategory::Tech ),
100 FUNCDATA( Imln, UNIQUE, STDPAR, 1, FDCategory::Tech ),
101 FUNCDATA( Imlog10, UNIQUE, STDPAR, 1, FDCategory::Tech ),
102 FUNCDATA( Imlog2, UNIQUE, STDPAR, 1, FDCategory::Tech ),
103 FUNCDATA( Improduct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
104 FUNCDATA( Imreal, UNIQUE, STDPAR, 1, FDCategory::Tech ),
105 FUNCDATA( Imsin, UNIQUE, STDPAR, 1, FDCategory::Tech ),
106 FUNCDATA( Imsub, UNIQUE, STDPAR, 2, FDCategory::Tech ),
107 FUNCDATA( Imsqrt, UNIQUE, STDPAR, 1, FDCategory::Tech ),
108 FUNCDATA( Imsum, UNIQUE, INTPAR, 1, FDCategory::Tech ),
109 FUNCDATA( Imtan, UNIQUE, STDPAR, 1, FDCategory::Tech ),
110 FUNCDATA( Imsec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
111 FUNCDATA( Imcsc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
112 FUNCDATA( Imcot, UNIQUE, STDPAR, 1, FDCategory::Tech ),
113 FUNCDATA( Imsinh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
114 FUNCDATA( Imcosh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
115 FUNCDATA( Imsech, UNIQUE, STDPAR, 1, FDCategory::Tech ),
116 FUNCDATA( Imcsch, UNIQUE, STDPAR, 1, FDCategory::Tech ),
117 FUNCDATA( Complex, UNIQUE, STDPAR, 3, FDCategory::Tech ),
118 FUNCDATA( Convert, UNIQUE, STDPAR, 3, FDCategory::Tech ),
119 FUNCDATA( Amordegrc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
120 FUNCDATA( Amorlinc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
121 FUNCDATA( Accrint, UNIQUE, INTPAR, 7, FDCategory::Finance ),
122 FUNCDATA( Accrintm, UNIQUE, INTPAR, 5, FDCategory::Finance ),
123 FUNCDATA( Received, UNIQUE, INTPAR, 5, FDCategory::Finance ),
124 FUNCDATA( Disc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
125 FUNCDATA( Duration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
126 FUNCDATA( Effect, DOUBLE, STDPAR, 2, FDCategory::Finance ),
127 FUNCDATA( Cumprinc, DOUBLE, STDPAR, 6, FDCategory::Finance ),
128 FUNCDATA( Cumipmt, DOUBLE, STDPAR, 6, FDCategory::Finance ),
129 FUNCDATA( Price, UNIQUE, INTPAR, 7, FDCategory::Finance ),
130 FUNCDATA( Pricedisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
131 FUNCDATA( Pricemat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
132 FUNCDATA( Mduration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
133 FUNCDATA( Nominal, DOUBLE, STDPAR, 2, FDCategory::Finance ),
134 FUNCDATA( Dollarfr, UNIQUE, STDPAR, 2, FDCategory::Finance ),
135 FUNCDATA( Dollarde, UNIQUE, STDPAR, 2, FDCategory::Finance ),
136 FUNCDATA( Yield, UNIQUE, INTPAR, 7, FDCategory::Finance ),
137 FUNCDATA( Yielddisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
138 FUNCDATA( Yieldmat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
139 FUNCDATA( Tbilleq, UNIQUE, INTPAR, 3, FDCategory::Finance ),
140 FUNCDATA( Tbillprice, UNIQUE, INTPAR, 3, FDCategory::Finance ),
141 FUNCDATA( Tbillyield, UNIQUE, INTPAR, 3, FDCategory::Finance ),
142 FUNCDATA( Oddfprice, UNIQUE, INTPAR, 9, FDCategory::Finance ),
143 FUNCDATA( Oddfyield, UNIQUE, INTPAR, 9, FDCategory::Finance ),
144 FUNCDATA( Oddlprice, UNIQUE, INTPAR, 8, FDCategory::Finance ),
145 FUNCDATA( Oddlyield, UNIQUE, INTPAR, 8, FDCategory::Finance ),
146 FUNCDATA( Xirr, UNIQUE, INTPAR, 3, FDCategory::Finance ),
147 FUNCDATA( Xnpv, UNIQUE, STDPAR, 3, FDCategory::Finance ),
148 FUNCDATA( Intrate, UNIQUE, INTPAR, 5, FDCategory::Finance ),
149 FUNCDATA( Coupncd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
150 FUNCDATA( Coupdays, UNIQUE, INTPAR, 4, FDCategory::Finance ),
151 FUNCDATA( Coupdaysnc, UNIQUE, INTPAR, 4, FDCategory::Finance ),
152 FUNCDATA( Coupdaybs, UNIQUE, INTPAR, 4, FDCategory::Finance ),
153 FUNCDATA( Couppcd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
154 FUNCDATA( Coupnum, UNIQUE, INTPAR, 4, FDCategory::Finance ),
155 FUNCDATA( Fvschedule, UNIQUE, STDPAR, 2, FDCategory::Finance )
157 #undef FUNCDATA
159 namespace sca { namespace analysis {
161 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
163 if( (nMonth == 2) && IsLeapYear( nYear ) )
164 return 29;
165 static const sal_uInt16 aDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
166 return aDaysInMonth[ nMonth ];
171 * Convert a date to a count of days starting from 01/01/0001
173 * The internal representation of a Date used in this Addin
174 * is the number of days between 01/01/0001 and the date
175 * this function converts a Day , Month, Year representation
176 * to this internal Date value.
180 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
182 sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365;
183 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
185 for( sal_uInt16 i = 1; i < nMonth; i++ )
186 nDays += DaysInMonth(i,nYear);
187 nDays += nDay;
189 return nDays;
194 * Convert a count of days starting from 01/01/0001 to a date
196 * The internal representation of a Date used in this Addin
197 * is the number of days between 01/01/0001 and the date
198 * this function converts this internal Date value
199 * to a Day , Month, Year representation of a Date.
203 void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
205 if( nDays < 0 )
206 throw lang::IllegalArgumentException();
208 sal_Int32 nTempDays;
209 sal_Int32 i = 0;
210 bool bCalc;
214 nTempDays = nDays;
215 rYear = static_cast<sal_uInt16>((nTempDays / 365) - i);
216 nTempDays -= (static_cast<sal_Int32>(rYear) -1) * 365;
217 nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
218 bCalc = false;
219 if ( nTempDays < 1 )
221 i++;
222 bCalc = true;
224 else
226 if ( nTempDays > 365 )
228 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
230 i--;
231 bCalc = true;
236 while ( bCalc );
238 rMonth = 1;
239 while ( nTempDays > DaysInMonth( rMonth, rYear ) )
241 nTempDays -= DaysInMonth( rMonth, rYear );
242 rMonth++;
244 rDay = static_cast<sal_uInt16>(nTempDays);
249 * Get the null date used by the spreadsheet document
251 * The internal representation of a Date used in this Addin
252 * is the number of days between 01/01/0001 and the date
253 * this function returns this internal Date value for the document null date
257 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt )
259 if( xOpt.is() )
263 uno::Any aAny = xOpt->getPropertyValue( "NullDate" );
264 util::Date aDate;
265 if( aAny >>= aDate )
266 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
268 catch( uno::Exception& )
273 // no null date available -> no calculations possible
274 throw uno::RuntimeException();
278 sal_Int32 GetDiffDate360(
279 sal_uInt16 nDay1, sal_uInt16 nMonth1, sal_uInt16 nYear1, bool bLeapYear1,
280 sal_uInt16 nDay2, sal_uInt16 nMonth2, sal_uInt16 nYear2,
281 bool bUSAMethod )
283 if( nDay1 == 31 )
284 nDay1--;
285 else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) )
286 nDay1 = 30;
288 if( nDay2 == 31 )
290 if( bUSAMethod && nDay1 != 30 )
292 nDay2 = 1;
293 if( nMonth2 == 12 )
295 nYear2++;
296 nMonth2 = 1;
298 else
299 nMonth2++;
301 else
302 nDay2 = 30;
305 return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360;
309 sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod )
311 nDate1 += nNullDate;
312 nDate2 += nNullDate;
314 sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;
316 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
317 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
319 return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod );
323 sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 )
325 sal_uInt16 nLeaps = 0;
326 for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ )
328 if( IsLeapYear( n ) )
329 nLeaps++;
332 sal_uInt32 nSum = 1;
333 nSum += nYear2;
334 nSum -= nYear1;
335 nSum *= 365;
336 nSum += nLeaps;
338 return nSum;
342 sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode,
343 sal_Int32* pOptDaysIn1stYear )
345 bool bNeg = nStartDate > nEndDate;
347 if( bNeg )
349 sal_Int32 n = nEndDate;
350 nEndDate = nStartDate;
351 nStartDate = n;
354 sal_Int32 nRet;
356 switch( nMode )
358 case 0: // 0=USA (NASD) 30/360
359 case 4: // 4=Europe 30/360
361 sal_uInt16 nD1, nM1, nY1, nD2, nM2, nY2;
363 nStartDate += nNullDate;
364 nEndDate += nNullDate;
366 DaysToDate( nStartDate, nD1, nM1, nY1 );
367 DaysToDate( nEndDate, nD2, nM2, nY2 );
369 bool bLeap = IsLeapYear( nY1 );
370 sal_Int32 nDays, nMonths;
372 nMonths = nM2 - nM1;
373 nDays = nD2 - nD1;
375 nMonths += ( nY2 - nY1 ) * 12;
377 nRet = nMonths * 30 + nDays;
378 if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )
379 nRet -= bLeap? 1 : 2;
381 if( pOptDaysIn1stYear )
382 *pOptDaysIn1stYear = 360;
384 break;
385 case 1: // 1=exact/exact
386 if( pOptDaysIn1stYear )
388 sal_uInt16 nD, nM, nY;
390 DaysToDate( nStartDate + nNullDate, nD, nM, nY );
392 *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;
394 nRet = nEndDate - nStartDate;
395 break;
396 case 2: // 2=exact/360
397 nRet = nEndDate - nStartDate;
398 if( pOptDaysIn1stYear )
399 *pOptDaysIn1stYear = 360;
400 break;
401 case 3: //3=exact/365
402 nRet = nEndDate - nStartDate;
403 if( pOptDaysIn1stYear )
404 *pOptDaysIn1stYear = 365;
405 break;
406 default:
407 throw lang::IllegalArgumentException();
410 return bNeg? -nRet : nRet;
414 double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
416 sal_Int32 nDays1stYear;
417 sal_Int32 nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear );
419 return double( nTotalDays ) / double( nDays1stYear );
423 sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode )
425 switch( nMode )
427 case 0: // 0=USA (NASD) 30/360
428 case 2: // 2=exact/360
429 case 4: // 4=Europe 30/360
430 return 360;
431 case 1: // 1=exact/exact
433 sal_uInt16 nD, nM, nY;
434 nDate += nNullDate;
435 DaysToDate( nDate, nD, nM, nY );
436 return IsLeapYear( nY )? 366 : 365;
438 case 3: //3=exact/365
439 return 365;
440 default:
441 throw lang::IllegalArgumentException();
446 // tdf69569 making code compliant with change request for ODFF1.2 par 4.11.7.7
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_uInt16 i = nYear1; i <= nYear2; i++ )
542 nDayCount += ( IsLeapYear( i ) ? 366 : 365 );
544 nDaysInYear = static_cast<double>(nDayCount) / static_cast<double>( nYear2 - nYear1 + 1 );
546 else
548 // as a consequence, !isYearDifferent or
549 // nYear2 == nYear + 1 and (nMonth1 > nMonth2 or
550 // (nMonth1 == nMonth2 and nDay1 >= nDay2))
551 assert( ( !isYearDifferent ||
552 ( nYear1 + 1 == nYear2 &&
553 ( nMonth1 > nMonth2 ||
554 ( nMonth1 == nMonth2 || nDay1 >= nDay2 ) ) ) ) );
556 // ODFv1.2 part 2 section 4.11.7.7.8 (CHANGE REQUEST PENDING, see tdf6959)
557 if ( !isYearDifferent && IsLeapYear( nYear1 ) )
559 nDaysInYear = 366;
561 else
563 // ODFv1.2 part 2 section 4.11.7.7.9/10 (CHANGE REQUEST PENDING, see tdf69569)
564 // we need to determine whether there is a 29 February
565 // between nDate1 (inclusive) and nDate2 (inclusive)
566 // the case of nYear1 == nYear2 is adequately tested in previous test
567 if( isYearDifferent &&
568 ( ( IsLeapYear( nYear1 ) &&
569 ( ( nMonth1 < 2 ) || ( ( nMonth1 == 2 ) && ( nDay1 <= 29 ) ) ) ) ||
570 ( IsLeapYear( nYear2 ) &&
571 ( nMonth2 > 2 || ( ( nMonth2 == 2 ) && ( nDay2 == 29 ) ) ) ) ) )
573 nDaysInYear = 366;
575 else
577 nDaysInYear = 365;
582 break;
583 case 3: // 3=exact/365
584 nDaysInYear = 365;
585 break;
586 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
587 default:
588 throw lang::IllegalArgumentException();
591 return double( nDayDiff ) / nDaysInYear;
594 double BinomialCoefficient( double n, double k )
596 // This method is a copy of BinomKoeff()
597 // found in sc/source/core/tool/interpr3.cxx
599 double nVal = 0.0;
600 k = ::rtl::math::approxFloor(k);
601 if (n < k)
602 nVal = 0.0;
603 else if (k == 0.0)
604 nVal = 1.0;
605 else
607 nVal = n/k;
608 n--;
609 k--;
610 while (k > 0.0)
612 nVal *= n/k;
613 k--;
614 n--;
617 return nVal;
620 double GetGcd( double f1, double f2 )
622 double f = fmod( f1, f2 );
623 while( f > 0.0 )
625 f1 = f2;
626 f2 = f;
627 f = fmod( f1, f2 );
630 return f2;
634 double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim )
636 if ( nBase < 2 || nBase > 36 )
637 throw lang::IllegalArgumentException();
639 sal_uInt32 nStrLen = aStr.getLength();
640 if( nStrLen > nCharLim )
641 throw lang::IllegalArgumentException();
642 else if( !nStrLen )
643 return 0.0;
645 double fVal = 0.0;
647 const sal_Unicode* p = aStr.getStr();
649 sal_uInt16 nFirstDig = 0;
650 bool bFirstDig = true;
651 double fBase = nBase;
653 while ( *p )
655 sal_uInt16 n;
657 if( '0' <= *p && *p <= '9' )
658 n = *p - '0';
659 else if( 'A' <= *p && *p <= 'Z' )
660 n = 10 + ( *p - 'A' );
661 else if ( 'a' <= *p && *p <= 'z' )
662 n = 10 + ( *p - 'a' );
663 else
664 n = nBase;
666 if( n >= nBase )
667 throw lang::IllegalArgumentException(); // illegal char!
669 if( bFirstDig )
671 bFirstDig = false;
672 nFirstDig = n;
674 fVal = fVal * fBase + double( n );
676 p++;
680 if( nStrLen == nCharLim && !bFirstDig && (nFirstDig >= nBase / 2) )
681 { // handling negative values
682 fVal = ( pow( double( nBase ), double( nCharLim ) ) - fVal ); // complement
683 fVal *= -1.0;
686 return fVal;
690 static sal_Char GetMaxChar( sal_uInt16 nBase )
692 const sal_Char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
693 return c[ nBase ];
697 OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase,
698 sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces )
700 fNum = ::rtl::math::approxFloor( fNum );
701 fMin = ::rtl::math::approxFloor( fMin );
702 fMax = ::rtl::math::approxFloor( fMax );
704 if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) )
705 throw lang::IllegalArgumentException();
707 sal_Int64 nNum = static_cast< sal_Int64 >( fNum );
708 bool bNeg = nNum < 0;
709 if( bNeg )
710 nNum = sal_Int64( pow( double( nBase ), double( nMaxPlaces ) ) ) + nNum;
712 OUString aRet( OUString::number( nNum, nBase ).toAsciiUpperCase() );
715 if( bUsePlaces )
717 sal_Int32 nLen = aRet.getLength();
718 if( !bNeg && nLen > nPlaces )
720 throw lang::IllegalArgumentException();
722 else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) )
724 sal_Int32 nLeft = nPlaces - nLen;
725 std::unique_ptr<sal_Char[]> p( new sal_Char[ nLeft + 1 ] );
726 memset( p.get(), bNeg ? GetMaxChar( nBase ) : '0', nLeft );
727 p[ nLeft ] = 0x00;
728 OUString aTmp( p.get(), nLeft, RTL_TEXTENCODING_MS_1252 );
729 aTmp += aRet;
730 aRet = aTmp;
734 return aRet;
737 // implementation moved to module sal, see #i97091#
738 double Erf( double x )
740 return ::rtl::math::erf(x);
743 // implementation moved to module sal, see #i97091#
744 double Erfc( double x )
746 return ::rtl::math::erfc(x);
749 static bool IsNum( sal_Unicode c )
751 return c >= '0' && c <= '9';
755 static bool IsComma( sal_Unicode c )
757 return c == '.' || c == ',';
761 static bool IsExpStart( sal_Unicode c )
763 return c == 'e' || c == 'E';
767 static bool IsImagUnit( sal_Unicode c )
769 return c == 'i' || c == 'j';
773 static sal_uInt16 GetVal( sal_Unicode c )
775 return sal_uInt16( c - '0' );
779 bool ParseDouble( const sal_Unicode*& rp, double& rRet )
781 double fInt = 0.0;
782 double fFrac = 0.0;
783 double fMult = 0.1; // multiplier to multiply digits with, when adding fractional ones
784 sal_Int32 nExp = 0;
785 sal_Int32 nMaxExp = 307;
786 sal_uInt16 nDigCnt = 18; // max. number of digits to read in, rest doesn't matter
788 enum State { S_End = 0, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp };
790 State eS = S_Sign;
792 bool bNegNum = false;
793 bool bNegExp = false;
795 const sal_Unicode* p = rp;
796 sal_Unicode c;
798 while( eS )
800 c = *p;
801 switch( eS )
803 case S_Sign:
804 if( IsNum( c ) )
806 fInt = GetVal( c );
807 nDigCnt--;
808 eS = S_Int;
810 else if( c == '-' )
812 bNegNum = true;
813 eS = S_IntStart;
815 else if( c == '+' )
816 eS = S_IntStart;
817 else if( IsComma( c ) )
818 eS = S_Frac;
819 else
820 return false;
821 break;
822 case S_IntStart:
823 if( IsNum( c ) )
825 fInt = GetVal( c );
826 nDigCnt--;
827 eS = S_Int;
829 else if( IsComma( c ) )
830 eS = S_Frac;
831 else if( IsImagUnit( c ) )
833 rRet = 0.0;
834 return true;
836 else
837 return false;
838 break;
839 case S_Int:
840 if( IsNum( c ) )
842 fInt *= 10.0;
843 fInt += double( GetVal( c ) );
844 nDigCnt--;
845 if( !nDigCnt )
846 eS = S_IgnoreIntDigs;
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_IgnoreIntDigs:
856 if( IsNum( c ) )
857 nExp++; // just multiply num with 10... ;-)
858 else if( IsComma( c ) )
859 eS = S_Frac;
860 else if( IsExpStart( c ) )
861 eS = S_ExpSign;
862 else
863 eS = S_End;
864 break;
865 case S_Frac:
866 if( IsNum( c ) )
868 fFrac += double( GetVal( c ) ) * fMult;
869 nDigCnt--;
870 if( nDigCnt )
871 fMult *= 0.1;
872 else
873 eS = S_IgnoreFracDigs;
875 else if( IsExpStart( c ) )
876 eS = S_ExpSign;
877 else
878 eS = S_End;
879 break;
880 case S_IgnoreFracDigs:
881 if( IsExpStart( c ) )
882 eS = S_ExpSign;
883 else if( !IsNum( c ) )
884 eS = S_End;
885 break;
886 case S_ExpSign:
887 if( IsNum( c ) )
889 nExp = GetVal( c );
890 eS = S_Exp;
892 else if( c == '-' )
894 bNegExp = true;
895 eS = S_Exp;
897 else if( c != '+' )
898 eS = S_End;
899 break;
900 case S_Exp:
901 if( IsNum( c ) )
903 nExp *= 10;
904 nExp += GetVal( c );
905 if( nExp > nMaxExp )
906 return false;
908 else
909 eS = S_End;
910 break;
911 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
912 case S_End:
913 break;
916 p++;
919 p--; // set pointer back to last
920 rp = p;
922 fInt += fFrac;
924 if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error
926 sal_Int32 nLog10 = sal_Int32( log10( fInt ) );
928 if( bNegExp )
929 nExp = -nExp;
931 if( nLog10 + nExp > nMaxExp )
932 return false;
934 fInt = ::rtl::math::pow10Exp( fInt, nExp );
937 if( bNegNum )
938 fInt = -fInt;
940 rRet = fInt;
942 return true;
946 OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig )
948 const int nBuff = 256;
949 sal_Char aBuff[ nBuff + 1 ];
950 const char* pFormStr = bLeadingSign? "%+.*g" : "%.*g";
951 int nLen = snprintf( aBuff, nBuff, pFormStr, int( nMaxDig ), f );
952 // you never know which underlying implementation you get ...
953 aBuff[nBuff] = 0;
954 if ( nLen < 0 || nLen > nBuff )
955 nLen = strlen( aBuff );
957 OUString aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 );
959 return aRet;
963 double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
964 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
966 sal_uInt32 nPer = sal_uInt32( fPer );
967 double fUsePer = 1.0 / fRate;
968 double fAmorCoeff;
970 if( fUsePer < 3.0 )
971 fAmorCoeff = 1.0;
972 else if( fUsePer < 5.0 )
973 fAmorCoeff = 1.5;
974 else if( fUsePer <= 6.0 )
975 fAmorCoeff = 2.0;
976 else
977 fAmorCoeff = 2.5;
979 fRate *= fAmorCoeff;
980 double fNRate = ::rtl::math::round( GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost );
981 fCost -= fNRate;
982 double fRest = fCost - fRestVal; // aboriginal cost - residual value - sum of all write-downs
984 for( sal_uInt32 n = 0 ; n < nPer ; n++ )
986 fNRate = ::rtl::math::round( fRate * fCost );
987 fRest -= fNRate;
989 if( fRest < 0.0 )
991 switch( nPer - n )
993 case 0:
994 case 1:
995 return ::rtl::math::round( fCost * 0.5 );
996 default:
997 return 0.0;
1001 fCost -= fNRate;
1004 return fNRate;
1008 double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
1009 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
1011 sal_uInt32 nPer = sal_uInt32( fPer );
1012 double fOneRate = fCost * fRate;
1013 double fCostDelta = fCost - fRestVal;
1014 double f0Rate = GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost;
1015 sal_uInt32 nNumOfFullPeriods = sal_uInt32( ( fCost - fRestVal - f0Rate) / fOneRate );
1017 double fResult = 0.0;
1018 if( nPer == 0 )
1019 fResult = f0Rate;
1020 else if( nPer <= nNumOfFullPeriods )
1021 fResult = fOneRate;
1022 else if( nPer == nNumOfFullPeriods + 1 )
1023 fResult = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
1025 if ( fResult > 0.0 )
1026 return fResult;
1027 else
1028 return 0.0;
1032 double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup,
1033 double fYield, sal_Int32 nFreq, sal_Int32 nBase )
1035 double fYearfrac = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1036 double fNumOfCoups = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1037 double fDur = 0.0;
1038 const double f100 = 100.0;
1039 fCoup *= f100 / double( nFreq ); // fCoup is used as cash flow
1040 fYield /= nFreq;
1041 fYield += 1.0;
1043 double nDiff = fYearfrac * nFreq - fNumOfCoups;
1045 double t;
1047 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1048 fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff );
1050 fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1052 double p = 0.0;
1053 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1054 p += fCoup / pow( fYield, t + nDiff );
1056 p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1058 fDur /= p;
1059 fDur /= double( nFreq );
1061 return fDur;
1065 double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue,
1066 double fRate, double fPrice, sal_Int32 nBase )
1068 double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );
1069 double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );
1070 double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1072 double y = 1.0 + fIssMat * fRate;
1073 y /= fPrice / 100.0 + fIssSet * fRate;
1074 y--;
1075 y /= fSetMat;
1077 return y;
1081 double GetOddfprice( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1082 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fYield*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1083 sal_Int32 /*nBase*/ )
1085 // If you change this to not unconditionally throw, the
1086 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1087 // financial.cxx can be removed.
1088 throw uno::RuntimeException();
1092 double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice,
1093 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1095 double fRate = fCoup;
1096 double fPriceN = 0.0;
1097 double fYield1 = 0.0;
1098 double fYield2 = 1.0;
1099 double fPrice1 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield1, fRedemp, nFreq, nBase );
1100 double fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1101 double fYieldN = ( fYield2 - fYield1 ) * 0.5;
1103 for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ )
1105 fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase );
1107 if( rtl::math::approxEqual(fPrice, fPrice1) )
1108 return fYield1;
1109 else if( rtl::math::approxEqual(fPrice, fPrice2) )
1110 return fYield2;
1111 else if( rtl::math::approxEqual(fPrice, fPriceN) )
1112 return fYieldN;
1113 else if( fPrice < fPrice2 )
1115 fYield2 *= 2.0;
1116 fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1118 fYieldN = ( fYield2 - fYield1 ) * 0.5;
1120 else
1122 if( fPrice < fPriceN )
1124 fYield1 = fYieldN;
1125 fPrice1 = fPriceN;
1127 else
1129 fYield2 = fYieldN;
1130 fPrice2 = fPriceN;
1133 fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) );
1137 if( fabs( fPrice - fPriceN ) > fPrice / 100.0 )
1138 throw lang::IllegalArgumentException(); // result not precise enough
1140 return fYieldN;
1144 double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield,
1145 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1147 double fFreq = nFreq;
1149 double fE = GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase );
1150 double fDSC_E = GetCoupdaysnc( nNullDate, nSettle, nMat, nFreq, nBase ) / fE;
1151 double fN = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1152 double fA = GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1154 double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) );
1155 fRet -= 100.0 * fRate / fFreq * fA / fE;
1157 double fT1 = 100.0 * fRate / fFreq;
1158 double fT2 = 1.0 + fYield / fFreq;
1160 for( double fK = 0.0 ; fK < fN ; fK++ )
1161 fRet += fT1 / pow( fT2, fK + fDSC_E );
1163 return fRet;
1167 double GetOddfyield( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1168 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fPrice*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1169 sal_Int32 /*nBase*/ )
1171 // If you change this to not unconditionally throw, the
1172 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1173 // financial.cxx can be removed.
1174 throw uno::RuntimeException();
1178 double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1179 double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1181 double fFreq = double( nFreq );
1182 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1183 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1184 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1186 double p = fRedemp + fDCi * 100.0 * fRate / fFreq;
1187 p /= fDSCi * fYield / fFreq + 1.0;
1188 p -= fAi * 100.0 * fRate / fFreq;
1190 return p;
1194 double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1195 double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1197 double fFreq = double( nFreq );
1198 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1199 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1200 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1202 double y = fRedemp + fDCi * 100.0 * fRate / fFreq;
1203 y /= fPrice + fAi * 100.0 * fRate / fFreq;
1204 y--;
1205 y *= fFreq / fDSCi;
1207 return y;
1211 double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType )
1213 double fPmt;
1214 if( fRate == 0.0 )
1215 fPmt = ( fPv + fFv ) / fNper;
1216 else
1218 double fTerm = pow( 1.0 + fRate, fNper );
1219 if( nPayType > 0 )
1220 fPmt = ( fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fRate );
1221 else
1222 fPmt = fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm );
1225 return -fPmt;
1229 double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType )
1231 double fFv;
1232 if( fRate == 0.0 )
1233 fFv = fPv + fPmt * fNper;
1234 else
1236 double fTerm = pow( 1.0 + fRate, fNper );
1237 if( nPayType > 0 )
1238 fFv = fPv * fTerm + fPmt * ( 1.0 + fRate ) * ( fTerm - 1.0 ) / fRate;
1239 else
1240 fFv = fPv * fTerm + fPmt * ( fTerm - 1.0 ) / fRate;
1243 return -fFv;
1246 // financial functions COUP***
1248 // COUPPCD: find last coupon date before settlement (can be equal to settlement)
1249 /// @throws css::lang::IllegalArgumentException
1250 static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1252 rDate = rMat;
1253 rDate.setYear( rSettle.getYear() );
1254 if( rDate < rSettle )
1255 rDate.addYears( 1 );
1256 while( rDate > rSettle )
1257 rDate.addMonths( -12 / nFreq );
1260 double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1262 if( nSettle >= nMat || CHK_Freq )
1263 throw lang::IllegalArgumentException();
1265 ScaDate aDate;
1266 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1267 return aDate.getDate( nNullDate );
1270 // COUPNCD: find first coupon date after settlement (is never equal to settlement)
1271 /// @throws css::lang::IllegalArgumentException
1272 static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1274 rDate = rMat;
1275 rDate.setYear( rSettle.getYear() );
1276 if( rDate > rSettle )
1277 rDate.addYears( -1 );
1278 while( rDate <= rSettle )
1279 rDate.addMonths( 12 / nFreq );
1282 double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1284 if( nSettle >= nMat || CHK_Freq )
1285 throw lang::IllegalArgumentException();
1287 ScaDate aDate;
1288 lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1289 return aDate.getDate( nNullDate );
1292 // COUPDAYBS: get day count: coupon date before settlement <-> settlement
1293 double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1295 if( nSettle >= nMat || CHK_Freq )
1296 throw lang::IllegalArgumentException();
1298 ScaDate aSettle( nNullDate, nSettle, nBase );
1299 ScaDate aDate;
1300 lcl_GetCouppcd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1301 return ScaDate::getDiff( aDate, aSettle );
1304 // COUPDAYSNC: get day count: settlement <-> coupon date after settlement
1305 double GetCoupdaysnc( 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 if( (nBase != 0) && (nBase != 4) )
1312 ScaDate aSettle( nNullDate, nSettle, nBase );
1313 ScaDate aDate;
1314 lcl_GetCoupncd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1315 return ScaDate::getDiff( aSettle, aDate );
1317 return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1320 // COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement
1321 double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1323 if( nSettle >= nMat || CHK_Freq )
1324 throw lang::IllegalArgumentException();
1326 if( nBase == 1 )
1328 ScaDate aDate;
1329 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1330 ScaDate aNextDate( aDate );
1331 aNextDate.addMonths( 12 / nFreq );
1332 return ScaDate::getDiff( aDate, aNextDate );
1334 return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq;
1337 // COUPNUM: get count of coupon dates
1338 double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1340 if( nSettle >= nMat || CHK_Freq )
1341 throw lang::IllegalArgumentException();
1343 ScaDate aMat( nNullDate, nMat, nBase );
1344 ScaDate aDate;
1345 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), aMat, nFreq );
1346 sal_uInt16 nMonths = (aMat.getYear() - aDate.getYear()) * 12 + aMat.getMonth() - aDate.getMonth();
1347 return static_cast< double >( nMonths * nFreq / 12 );
1350 FuncData::FuncData(const FuncDataBase& r) :
1351 aIntName( OUString::createFromAscii( r.pIntName ) ),
1352 pUINameID( r.pUINameID ),
1353 pDescrID( r.pDescrID ),
1354 bDouble( r.bDouble ),
1355 bWithOpt( r.bWithOpt ),
1356 nParam( r.nNumOfParams ),
1357 eCat( r.eCat )
1359 if (r.pSuffix)
1360 aSuffix = OUString::createFromAscii(r.pSuffix);
1362 aCompList.resize(2);
1363 aCompList[0] = OUString(r.pCompListID[0], strlen(r.pCompListID[0]), RTL_TEXTENCODING_UTF8);
1364 aCompList[1] = OUString(r.pCompListID[1], strlen(r.pCompListID[1]), RTL_TEXTENCODING_UTF8);
1367 FuncData::~FuncData()
1371 sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const
1373 if( !bWithOpt )
1374 nParamNum++;
1376 if( nParamNum > nParam )
1377 return nParam * 2;
1378 else
1379 return nParamNum * 2;
1382 void InitFuncDataList(FuncDataList& rList)
1384 for(const auto & rFuncData : pFuncDatas)
1385 rList.push_back(FuncData(rFuncData));
1388 SortedIndividualInt32List::SortedIndividualInt32List()
1393 SortedIndividualInt32List::~SortedIndividualInt32List()
1398 void SortedIndividualInt32List::Insert( sal_Int32 nDay )
1400 sal_uInt32 nIndex = Count();
1401 while( nIndex )
1403 nIndex--;
1404 sal_Int32 nRef = Get( nIndex );
1405 if( nDay == nRef )
1406 return;
1407 else if( nDay > nRef )
1409 maVector.insert( maVector.begin() + nIndex + 1, nDay );
1410 return;
1413 maVector.insert( maVector.begin(), nDay );
1417 void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1419 if( !nDay )
1420 return;
1422 nDay += nNullDate;
1423 if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) )
1424 Insert( nDay );
1428 void SortedIndividualInt32List::Insert(
1429 double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1431 if( (fDay < -2147483648.0) || (fDay > 2147483649.0) )
1432 throw lang::IllegalArgumentException();
1433 Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend );
1437 bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const
1439 sal_uInt32 nE = Count();
1441 if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) )
1442 return false;
1444 // linear search
1446 for( sal_uInt32 n = 0 ; n < nE ; n++ )
1448 sal_Int32 nRef = Get( n );
1450 if( nRef == nVal )
1451 return true;
1452 else if( nRef > nVal )
1453 return false;
1455 return false;
1459 void SortedIndividualInt32List::InsertHolidayList(
1460 const ScaAnyConverter& rAnyConv,
1461 const uno::Any& rHolAny,
1462 sal_Int32 nNullDate,
1463 bool bInsertOnWeekend )
1465 double fDay;
1466 if( rAnyConv.getDouble( fDay, rHolAny ) )
1467 Insert( fDay, nNullDate, bInsertOnWeekend );
1471 void SortedIndividualInt32List::InsertHolidayList(
1472 ScaAnyConverter& rAnyConv,
1473 const uno::Reference< beans::XPropertySet >& xOptions,
1474 const uno::Any& rHolAny,
1475 sal_Int32 nNullDate )
1477 rAnyConv.init( xOptions );
1478 if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE )
1480 uno::Sequence< uno::Sequence< uno::Any > > aAnySeq;
1481 if( !(rHolAny >>= aAnySeq) )
1482 throw lang::IllegalArgumentException();
1484 const uno::Sequence< uno::Any >* pSeqArray = aAnySeq.getConstArray();
1485 for( sal_Int32 nIndex1 = 0; nIndex1 < aAnySeq.getLength(); nIndex1++ )
1487 const uno::Sequence< uno::Any >& rSubSeq = pSeqArray[ nIndex1 ];
1488 const uno::Any* pAnyArray = rSubSeq.getConstArray();
1490 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1491 InsertHolidayList( rAnyConv, pAnyArray[ nIndex2 ], nNullDate, false/*bInsertOnWeekend*/ );
1494 else
1495 InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ );
1499 void ScaDoubleList::Append(
1500 const uno::Sequence< uno::Sequence< double > >& rValueSeq )
1502 const uno::Sequence< double >* pSeqArray = rValueSeq.getConstArray();
1503 for( sal_Int32 nIndex1 = 0; nIndex1 < rValueSeq.getLength(); nIndex1++ )
1505 const uno::Sequence< double >& rSubSeq = pSeqArray[ nIndex1 ];
1506 const double* pArray = rSubSeq.getConstArray();
1507 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1508 Append( pArray[ nIndex2 ] );
1513 void ScaDoubleList::Append(
1514 const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq )
1516 const uno::Sequence< sal_Int32 >* pSeqArray = rValueSeq.getConstArray();
1517 for( sal_Int32 nIndex1 = 0; nIndex1 < rValueSeq.getLength(); nIndex1++ )
1519 const uno::Sequence< sal_Int32 >& rSubSeq = pSeqArray[ nIndex1 ];
1520 const sal_Int32* pArray = rSubSeq.getConstArray();
1521 for( sal_Int32 nIndex2 = 0; nIndex2 < rSubSeq.getLength(); nIndex2++ )
1522 Append( pArray[ nIndex2 ] );
1526 void ScaDoubleList::Append(
1527 const ScaAnyConverter& rAnyConv,
1528 const uno::Any& rAny,
1529 bool bIgnoreEmpty )
1531 if( auto s = o3tl::tryAccess<
1532 css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) )
1533 Append( rAnyConv, *s, bIgnoreEmpty );
1534 else
1536 double fValue;
1537 if( rAnyConv.getDouble( fValue, rAny ) )
1538 Append( fValue );
1539 else if( !bIgnoreEmpty )
1540 Append( 0.0 );
1545 void ScaDoubleList::Append(
1546 const ScaAnyConverter& rAnyConv,
1547 const uno::Sequence< uno::Any >& rAnySeq,
1548 bool bIgnoreEmpty )
1550 const uno::Any* pArray = rAnySeq.getConstArray();
1551 for( sal_Int32 nIndex = 0; nIndex < rAnySeq.getLength(); nIndex++ )
1552 Append( rAnyConv, pArray[ nIndex ], bIgnoreEmpty );
1556 void ScaDoubleList::Append(
1557 const ScaAnyConverter& rAnyConv,
1558 const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq,
1559 bool bIgnoreEmpty )
1561 const uno::Sequence< uno::Any >* pArray = rAnySeq.getConstArray();
1562 for( sal_Int32 nIndex = 0; nIndex < rAnySeq.getLength(); nIndex++ )
1563 Append( rAnyConv, pArray[ nIndex ], bIgnoreEmpty );
1566 void ScaDoubleList::Append(
1567 ScaAnyConverter& rAnyConv,
1568 const uno::Reference< beans::XPropertySet >& xOpt,
1569 const uno::Sequence< uno::Any >& rAnySeq )
1571 rAnyConv.init( xOpt );
1572 Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ );
1576 bool ScaDoubleList::CheckInsert( double ) const
1578 return true;
1582 bool ScaDoubleListGT0::CheckInsert( double fValue ) const
1584 if( fValue < 0.0 )
1585 throw lang::IllegalArgumentException();
1586 return fValue > 0.0;
1590 bool ScaDoubleListGE0::CheckInsert( double fValue ) const
1592 if( fValue < 0.0 )
1593 throw lang::IllegalArgumentException();
1594 return true;
1598 Complex::Complex( const OUString& rStr )
1600 if( !ParseString( rStr, *this ) )
1601 throw lang::IllegalArgumentException();
1605 inline bool Complex::IsImagUnit( sal_Unicode c )
1607 return c == 'i' || c == 'j';
1610 bool Complex::ParseString( const OUString& rStr, Complex& rCompl )
1612 rCompl.c = '\0'; // do not force a symbol, if only real part present
1614 const sal_Unicode* pStr = rStr.getStr();
1616 if( IsImagUnit( *pStr ) && rStr.getLength() == 1)
1618 rCompl.r = 0.0;
1619 rCompl.i = 1.0;
1620 rCompl.c = *pStr;
1621 return true;
1624 double f;
1626 if( !ParseDouble( pStr, f ) )
1627 return false;
1629 switch( *pStr )
1631 case '-': // imag part follows
1632 case '+':
1634 double r = f;
1635 if( IsImagUnit( pStr[ 1 ] ) )
1637 rCompl.c = pStr[ 1 ];
1638 if( pStr[ 2 ] == 0 )
1640 rCompl.r = f;
1641 rCompl.i = ( *pStr == '+' )? 1.0 : -1.0;
1642 return true;
1645 else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) )
1647 rCompl.c = *pStr;
1648 pStr++;
1649 if( *pStr == 0 )
1651 rCompl.r = r;
1652 rCompl.i = f;
1653 return true;
1657 break;
1658 case 'j':
1659 case 'i':
1660 rCompl.c = *pStr;
1661 pStr++;
1662 if( *pStr == 0 )
1664 rCompl.i = f;
1665 rCompl.r = 0.0;
1666 return true;
1668 break;
1669 case 0: // only real-part
1670 rCompl.r = f;
1671 rCompl.i = 0.0;
1672 return true;
1675 return false;
1679 OUString Complex::GetString() const
1681 CHK_FINITE(r);
1682 CHK_FINITE(i);
1683 OUStringBuffer aRet;
1685 bool bHasImag = i != 0.0;
1686 bool bHasReal = !bHasImag || (r != 0.0);
1688 if( bHasReal )
1689 aRet.append(::GetString( r, false ));
1690 if( bHasImag )
1692 if( i == 1.0 )
1694 if( bHasReal )
1695 aRet.append('+');
1697 else if( i == -1.0 )
1698 aRet.append('-');
1699 else
1700 aRet.append(::GetString( i, bHasReal ));
1701 aRet.append((c != 'j') ? 'i' : 'j');
1704 return aRet.makeStringAndClear();
1708 double Complex::Arg() const
1710 if( r == 0.0 && i == 0.0 )
1711 throw lang::IllegalArgumentException();
1713 double phi = acos( r / Abs() );
1715 if( i < 0.0 )
1716 phi = -phi;
1718 return phi;
1722 void Complex::Power( double fPower )
1724 if( r == 0.0 && i == 0.0 )
1726 if( fPower <= 0 )
1727 throw lang::IllegalArgumentException();
1728 r = i = 0.0;
1729 return;
1732 double p, phi;
1734 p = Abs();
1736 phi = acos( r / p );
1737 if( i < 0.0 )
1738 phi = -phi;
1740 p = pow( p, fPower );
1741 phi *= fPower;
1743 r = cos( phi ) * p;
1744 i = sin( phi ) * p;
1748 void Complex::Sqrt()
1750 static const double fMultConst = 0.7071067811865475; // ...2440084436210485 = 1/sqrt(2)
1751 double p = Abs();
1752 double i_ = sqrt( p - r ) * fMultConst;
1754 r = sqrt( p + r ) * fMultConst;
1755 i = ( i < 0.0 )? -i_ : i_;
1759 void Complex::Sin()
1761 if( !::rtl::math::isValidArcArg( r ) )
1762 throw lang::IllegalArgumentException();
1764 if( i )
1766 double r_;
1768 r_ = sin( r ) * cosh( i );
1769 i = cos( r ) * sinh( i );
1770 r = r_;
1772 else
1773 r = sin( r );
1777 void Complex::Cos()
1779 if( !::rtl::math::isValidArcArg( r ) )
1780 throw lang::IllegalArgumentException();
1782 if( i )
1784 double r_;
1786 r_ = cos( r ) * cosh( i );
1787 i = -( sin( r ) * sinh( i ) );
1788 r = r_;
1790 else
1791 r = cos( r );
1795 void Complex::Div( const Complex& z )
1797 if( z.r == 0 && z.i == 0 )
1798 throw lang::IllegalArgumentException();
1800 double a1 = r;
1801 double a2 = z.r;
1802 double b1 = i;
1803 double b2 = z.i;
1805 double f = 1.0 / ( a2 * a2 + b2 * b2 );
1807 r = ( a1 * a2 + b1 * b2 ) * f;
1808 i = ( a2 * b1 - a1 * b2 ) * f;
1810 if( !c ) c = z.c;
1814 void Complex::Exp()
1816 double fE = exp( r );
1817 r = fE * cos( i );
1818 i = fE * sin( i );
1822 void Complex::Ln()
1824 if( r == 0.0 && i == 0.0 )
1825 throw lang::IllegalArgumentException();
1827 double fAbs = Abs();
1828 bool bNegi = i < 0.0;
1830 i = acos( r / fAbs );
1832 if( bNegi )
1833 i = -i;
1835 r = log( fAbs );
1839 void Complex::Log10()
1841 Ln();
1842 Mult( 0.434294481903251828 ); // * log10( e )
1846 void Complex::Log2()
1848 Ln();
1849 Mult( 1.442695040888963407 ); // * log2( e )
1853 void Complex::Tan()
1855 if ( i )
1857 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1858 throw lang::IllegalArgumentException();
1859 double fScale =1.0 / ( cos( 2.0 * r ) + cosh( 2.0 * i ));
1860 r = sin( 2.0 * r ) * fScale;
1861 i = sinh( 2.0 * i ) * fScale;
1863 else
1865 if( !::rtl::math::isValidArcArg( r ) )
1866 throw lang::IllegalArgumentException();
1867 r = tan( r );
1872 void Complex::Sec()
1874 if( i )
1876 if( !::rtl::math::isValidArcArg( 2 * r ) )
1877 throw lang::IllegalArgumentException();
1878 double fScale = 1.0 / (cosh( 2.0 * i) + cos ( 2.0 * r));
1879 double r_;
1880 r_ = 2.0 * cos( r ) * cosh( i ) * fScale;
1881 i = 2.0 * sin( r ) * sinh( i ) * fScale;
1882 r = r_;
1884 else
1886 if( !::rtl::math::isValidArcArg( r ) )
1887 throw lang::IllegalArgumentException();
1888 r = 1.0 / cos( r );
1893 void Complex::Csc()
1895 if( i )
1897 if( !::rtl::math::isValidArcArg( 2 * r ) )
1898 throw lang::IllegalArgumentException();
1899 double fScale = 1.0 / (cosh( 2.0 * i) - cos ( 2.0 * r));
1900 double r_;
1901 r_ = 2.0 * sin( r ) * cosh( i ) * fScale;
1902 i = -2.0 * cos( r ) * sinh( i ) * fScale;
1903 r = r_;
1905 else
1907 if( !::rtl::math::isValidArcArg( r ) )
1908 throw lang::IllegalArgumentException();
1909 r = 1.0 / sin( r );
1914 void Complex::Cot()
1916 if ( i )
1918 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1919 throw lang::IllegalArgumentException();
1920 double fScale =1.0 / ( cosh( 2.0 * i ) - cos( 2.0 * r ) );
1921 r = sin( 2.0 * r ) * fScale;
1922 i = - ( sinh( 2.0 * i ) * fScale );
1924 else
1926 if( !::rtl::math::isValidArcArg( r ) )
1927 throw lang::IllegalArgumentException();
1928 r = 1.0 / tan( r );
1933 void Complex::Sinh()
1935 if( !::rtl::math::isValidArcArg( r ) )
1936 throw lang::IllegalArgumentException();
1938 if( i )
1940 double r_;
1941 r_ = sinh( r ) * cos( i );
1942 i = cosh( r ) * sin( i );
1943 r = r_;
1945 else
1946 r = sinh( r );
1950 void Complex::Cosh()
1952 if( !::rtl::math::isValidArcArg( r ) )
1953 throw lang::IllegalArgumentException();
1955 if( i )
1957 double r_;
1958 r_ = cosh( r ) * cos( i );
1959 i = sinh( r ) * sin( i );
1960 r = r_;
1962 else
1963 r = cosh( r );
1967 void Complex::Sech()
1969 if ( i )
1971 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1972 throw lang::IllegalArgumentException();
1973 double fScale =1.0 / ( cosh( 2.0 * r ) + cos( 2.0 * i ));
1974 double r_;
1975 r_ = 2.0 * cosh( r ) * cos( i ) * fScale;
1976 i = - (2.0 * sinh( r ) * sin( i ) * fScale );
1977 r = r_ ;
1979 else
1981 if( !::rtl::math::isValidArcArg( r ) )
1982 throw lang::IllegalArgumentException();
1983 r = 1.0 / cosh( r );
1988 void Complex::Csch()
1990 if ( i )
1992 if( !::rtl::math::isValidArcArg( 2.0 * r ) )
1993 throw lang::IllegalArgumentException();
1994 double fScale =1.0 / ( cosh( 2.0 * r ) - cos( 2.0 * i ));
1995 double r_;
1996 r_ = 2.0 * sinh( r ) * cos( i ) * fScale;
1997 i = - ( 2.0 * cosh( r ) * sin( i ) * fScale );
1998 r = r_ ;
2000 else
2002 if( !::rtl::math::isValidArcArg( r ) )
2003 throw lang::IllegalArgumentException();
2004 r = 1.0 / sinh( r );
2009 ComplexList::~ComplexList()
2014 void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r )
2016 sal_Int32 n1, n2;
2017 sal_Int32 nE1 = r.getLength();
2018 sal_Int32 nE2;
2020 for( n1 = 0 ; n1 < nE1 ; n1++ )
2022 const uno::Sequence< OUString >& rList = r[ n1 ];
2023 nE2 = rList.getLength();
2025 for( n2 = 0 ; n2 < nE2 ; n2++ )
2027 const OUString& rStr = rList[ n2 ];
2029 if( !rStr.isEmpty() )
2030 Append( Complex( rStr ) );
2036 void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars )
2038 sal_Int32 nEle = aMultPars.getLength();
2040 for( sal_Int32 i = 0 ; i < nEle ; i++ )
2042 const uno::Any& r = aMultPars[ i ];
2043 switch( r.getValueTypeClass() )
2045 case uno::TypeClass_VOID: break;
2046 case uno::TypeClass_STRING:
2048 auto pStr = o3tl::forceAccess<OUString>(r);
2050 if( !pStr->isEmpty() )
2051 Append( Complex( *pStr ) );
2053 break;
2054 case uno::TypeClass_DOUBLE:
2055 Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) );
2056 break;
2057 case uno::TypeClass_SEQUENCE:
2059 uno::Sequence< uno::Sequence< uno::Any > > aValArr;
2060 if( !(r >>= aValArr) )
2061 throw lang::IllegalArgumentException();
2063 sal_Int32 nE = aValArr.getLength();
2064 const uno::Sequence< uno::Any >* pArr = aValArr.getConstArray();
2065 for( sal_Int32 n = 0 ; n < nE ; n++ )
2066 Append( pArr[ n ] );
2068 break;
2069 default:
2070 throw lang::IllegalArgumentException();
2075 ConvertData::ConvertData(const sal_Char p[], double fC, ConvertDataClass e, bool bPrefSupport)
2076 : fConst(fC)
2077 , aName(p, strlen(p), RTL_TEXTENCODING_MS_1252)
2078 , eClass(e)
2079 , bPrefixSupport(bPrefSupport)
2083 ConvertData::~ConvertData()
2087 sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const
2089 OUString aStr = rRef;
2090 sal_Int32 nLen = rRef.getLength();
2091 sal_Int32 nIndex = rRef.lastIndexOf( '^' );
2092 if( nIndex > 0 && nIndex == ( nLen - 2 ) )
2093 aStr = aStr.copy( 0, nLen - 2 ) + OUStringLiteral1( aStr[ nLen - 1 ] );
2094 if( aName == aStr )
2095 return 0;
2096 else
2098 const sal_Unicode* p = aStr.getStr();
2100 nLen = aStr.getLength();
2101 bool bPref = bPrefixSupport;
2102 bool bOneChar = (bPref && nLen > 1 && (aName == p + 1));
2103 if (bOneChar || (bPref && nLen > 2 && (aName == p + 2) &&
2104 *p == 'd' && *(p+1) == 'a'))
2106 sal_Int16 n;
2107 switch( *p )
2109 case 'y': n = -24; break; // yocto
2110 case 'z': n = -21; break; // zepto
2111 case 'a': n = -18; break;
2112 case 'f': n = -15; break;
2113 case 'p': n = -12; break;
2114 case 'n': n = -9; break;
2115 case 'u': n = -6; break;
2116 case 'm': n = -3; break;
2117 case 'c': n = -2; break;
2118 case 'd':
2120 if ( bOneChar )
2121 n = -1; // deci
2122 else
2123 n = 1; // deca
2125 break;
2126 case 'e': n = 1; break;
2127 case 'h': n = 2; break;
2128 case 'k': n = 3; break;
2129 case 'M': n = 6; break;
2130 case 'G': n = 9; break;
2131 case 'T': n = 12; break;
2132 case 'P': n = 15; break;
2133 case 'E': n = 18; break;
2134 case 'Z': n = 21; break; // zetta
2135 case 'Y': n = 24; break; // yotta
2136 default:
2137 n = INV_MATCHLEV;
2140 // We could weed some nonsense out, ODFF doesn't say so though.
2141 #if 0
2142 if (n < 0 && Class() == CDC_Information)
2143 n = INV_MATCHLEV; // milli-bits doesn't make sense
2144 #endif
2146 //! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------
2147 if( n != INV_MATCHLEV )
2149 sal_Unicode cLast = p[ aStr.getLength() - 1 ];
2150 if( cLast == '2' )
2151 n *= 2;
2152 else if( cLast == '3' )
2153 n *= 3;
2155 //! </HACK> -------------------------------------------------------------------
2157 return n;
2159 else if ( nLen > 2 && ( aName == p + 2 ) && ( Class() == CDC_Information ) )
2161 const sal_Unicode* pStr = aStr.getStr();
2162 if ( *(pStr + 1) != 'i')
2163 return INV_MATCHLEV;
2164 sal_Int16 n;
2165 switch( *pStr )
2167 case 'k': n = 10; break;
2168 case 'M': n = 20; break;
2169 case 'G': n = 30; break;
2170 case 'T': n = 40; break;
2171 case 'P': n = 50; break;
2172 case 'E': n = 60; break;
2173 case 'Z': n = 70; break;
2174 case 'Y': n = 80; break;
2175 default:
2176 n = INV_MATCHLEV;
2178 return n;
2180 else
2181 return INV_MATCHLEV;
2186 double ConvertData::Convert(
2187 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2189 if( Class() != r.Class() )
2190 throw lang::IllegalArgumentException();
2192 bool bBinFromLev = ( nLevFrom > 0 && ( nLevFrom % 10 ) == 0 );
2193 bool bBinToLev = ( nLevTo > 0 && ( nLevTo % 10 ) == 0 );
2195 if ( Class() == CDC_Information && ( bBinFromLev || bBinToLev ) )
2197 if ( bBinFromLev && bBinToLev )
2199 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo );
2200 f *= r.fConst / fConst;
2201 if( nLevFrom )
2202 f *= pow( 2.0, nLevFrom );
2204 else if ( bBinFromLev )
2205 f *= ( r.fConst / fConst ) * ( pow( 2.0, nLevFrom ) / pow( 10.0, nLevTo ) );
2206 else
2207 f *= ( r.fConst / fConst ) * ( pow( 10.0, nLevFrom ) / pow( 2.0, nLevTo ) );
2208 return f;
2211 nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo ); // effective level
2213 f *= r.fConst / fConst;
2215 if( nLevFrom )
2216 f = ::rtl::math::pow10Exp( f, nLevFrom );
2218 return f;
2222 double ConvertData::ConvertFromBase( double f, sal_Int16 n ) const
2224 return ::rtl::math::pow10Exp( f * fConst, -n );
2227 ConvertDataLinear::~ConvertDataLinear()
2231 double ConvertDataLinear::Convert(
2232 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
2234 if( Class() != r.Class() )
2235 throw lang::IllegalArgumentException();
2236 return r.ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo );
2240 double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const
2242 if( n )
2243 f = ::rtl::math::pow10Exp( f, n );
2245 f /= fConst;
2246 f -= fOffs;
2248 return f;
2252 double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const
2254 f += fOffs;
2255 f *= fConst;
2257 if( n )
2258 f = ::rtl::math::pow10Exp( f, -n );
2260 return f;
2264 ConvertDataList::ConvertDataList()
2266 #define NEWD(str,unit,cl) maVector.emplace_back(new ConvertData(str,unit,cl))
2267 #define NEWDP(str,unit,cl) maVector.emplace_back(new ConvertData(str,unit,cl,true))
2268 #define NEWL(str,unit,offs,cl) maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl))
2269 #define NEWLP(str,unit,offs,cl) maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl,true))
2271 // *** are extra and not standard Excel Analysis Addin!
2273 // MASS: 1 Gram is...
2274 NEWDP( "g", 1.0000000000000000E00, CDC_Mass ); // Gram
2275 NEWD( "sg", 6.8522050005347800E-05, CDC_Mass ); // Pieces
2276 NEWD( "lbm", 2.2046229146913400E-03, CDC_Mass ); // Pound (commercial weight)
2277 NEWDP( "u", 6.0221370000000000E23, CDC_Mass ); // U (atomic mass)
2278 NEWD( "ozm", 3.5273971800362700E-02, CDC_Mass ); // Ounce (commercial weight)
2279 NEWD( "stone", 1.574730e-04, CDC_Mass ); // *** Stone
2280 NEWD( "ton", 1.102311e-06, CDC_Mass ); // *** Ton
2281 NEWD( "grain", 1.543236E01, CDC_Mass ); // *** Grain
2282 NEWD( "pweight", 7.054792E-01, CDC_Mass ); // *** Pennyweight
2283 NEWD( "hweight", 1.968413E-05, CDC_Mass ); // *** Hundredweight
2284 NEWD( "shweight", 2.204623E-05, CDC_Mass ); // *** Shorthundredweight
2285 NEWD( "brton", 9.842065E-07, CDC_Mass ); // *** Gross Registered Ton
2286 NEWD( "cwt", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight
2287 NEWD( "shweight", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight also
2288 NEWD( "uk_cwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight
2289 NEWD( "lcwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2290 NEWD( "hweight", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2291 NEWD( "uk_ton", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton
2292 NEWD( "LTON", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton also
2294 // LENGTH: 1 Meter is...
2295 NEWDP( "m", 1.0000000000000000E00, CDC_Length ); // Meter
2296 NEWD( "mi", 6.2137119223733397E-04, CDC_Length ); // Britsh Mile 6,21371192237333969617434184363e-4
2297 NEWD( "Nmi", 5.3995680345572354E-04, CDC_Length ); // Nautical Mile 5,39956803455723542116630669546e-4
2298 NEWD( "in", 3.9370078740157480E01, CDC_Length ); // Inch 39,37007874015748031496062992126
2299 NEWD( "ft", 3.2808398950131234E00, CDC_Length ); // Foot 3,2808398950131233595800524934383
2300 NEWD( "yd", 1.0936132983377078E00, CDC_Length ); // Yard 1,0936132983377077865266841644794
2301 NEWDP( "ang", 1.0000000000000000E10, CDC_Length ); // Angstroem
2302 NEWD( "Pica", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2303 NEWD( "picapt", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2304 NEWD( "pica", 2.36220472441E02, CDC_Length ); // pica (1/6 Inch)
2305 NEWD( "ell", 8.748906E-01, CDC_Length ); // *** Ell
2306 NEWDP( "parsec", 3.240779E-17, CDC_Length ); // *** Parsec
2307 NEWDP( "pc", 3.240779E-17, CDC_Length ); // *** Parsec also
2308 NEWDP( "lightyear", 1.0570234557732930E-16, CDC_Length ); // *** Light Year
2309 NEWDP( "ly", 1.0570234557732930E-16, CDC_Length ); // *** Light Year also
2310 NEWD( "survey_mi", 6.2136994949494949E-04, CDC_Length ); // U.S. survey mile
2312 // TIME: 1 Second is...
2313 NEWD( "yr", 3.1688087814028950E-08, CDC_Time ); // Year
2314 NEWD( "day", 1.1574074074074074E-05, CDC_Time ); // Day
2315 NEWD( "d", 1.1574074074074074E-05, CDC_Time ); // Day also
2316 NEWD( "hr", 2.7777777777777778E-04, CDC_Time ); // Hour
2317 NEWD( "mn", 1.6666666666666667E-02, CDC_Time ); // Minute
2318 NEWD( "min", 1.6666666666666667E-02, CDC_Time ); // Minute also
2319 NEWDP( "sec", 1.0000000000000000E00, CDC_Time ); // Second
2320 NEWDP( "s", 1.0000000000000000E00, CDC_Time ); // Second also
2322 // PRESSURE: 1 Pascal is...
2323 NEWDP( "Pa", 1.0000000000000000E00, CDC_Pressure ); // Pascal
2324 NEWDP( "atm", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere
2325 NEWDP( "at", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere also
2326 NEWDP( "mmHg", 7.5006170799862700E-03, CDC_Pressure ); // mm Hg (Mercury)
2327 NEWD( "Torr", 7.5006380000000000E-03, CDC_Pressure ); // *** Torr
2328 NEWD( "psi", 1.4503770000000000E-04, CDC_Pressure ); // *** Psi
2330 // FORCE: 1 Newton is...
2331 NEWDP( "N", 1.0000000000000000E00, CDC_Force ); // Newton
2332 NEWDP( "dyn", 1.0000000000000000E05, CDC_Force ); // Dyn
2333 NEWDP( "dy", 1.0000000000000000E05, CDC_Force ); // Dyn also
2334 NEWD( "lbf", 2.24808923655339E-01, CDC_Force ); // Pound-Force
2335 NEWDP( "pond", 1.019716E02, CDC_Force ); // *** Pond
2337 // ENERGY: 1 Joule is...
2338 NEWDP( "J", 1.0000000000000000E00, CDC_Energy ); // Joule
2339 NEWDP( "e", 1.0000000000000000E07, CDC_Energy ); // Erg -> https://en.wikipedia.org/wiki/Erg
2340 NEWDP( "c", 2.3900624947346700E-01, CDC_Energy ); // Thermodynamical Calorie
2341 NEWDP( "cal", 2.3884619064201700E-01, CDC_Energy ); // Calorie
2342 NEWDP( "eV", 6.2414570000000000E18, CDC_Energy ); // Electronvolt
2343 NEWDP( "ev", 6.2414570000000000E18, CDC_Energy ); // Electronvolt also
2344 NEWD( "HPh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours
2345 NEWD( "hh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours also
2346 NEWDP( "Wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours
2347 NEWDP( "wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours also
2348 NEWD( "flb", 2.37304222192651E01, CDC_Energy ); // Foot Pound
2349 NEWD( "BTU", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit
2350 NEWD( "btu", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit also
2352 // POWER: 1 Watt is...
2353 NEWDP( "W", 1.0000000000000000E00, CDC_Power ); // Watt
2354 NEWDP( "w", 1.0000000000000000E00, CDC_Power ); // Watt also
2355 NEWD( "HP", 1.341022E-03, CDC_Power ); // Horsepower
2356 NEWD( "h", 1.341022E-03, CDC_Power ); // Horsepower also
2357 NEWD( "PS", 1.359622E-03, CDC_Power ); // *** German Pferdestaerke
2359 // MAGNETISM: 1 Tesla is...
2360 NEWDP( "T", 1.0000000000000000E00, CDC_Magnetism ); // Tesla
2361 NEWDP( "ga", 1.0000000000000000E04, CDC_Magnetism ); // Gauss
2363 // TEMPERATURE: 1 Kelvin is...
2364 NEWL( "C", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius
2365 NEWL( "cel", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius also
2366 NEWL( "F", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit
2367 NEWL( "fah", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit also
2368 NEWLP( "K", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin
2369 NEWLP( "kel", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin also
2370 NEWL( "Reau", 8.0000000000000000E-01, -2.7315000000000000E02, CDC_Temperature ); // *** Reaumur
2371 NEWL( "Rank", 1.8000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // *** Rankine
2373 // VOLUME: 1 Liter is...
2374 NEWD( "tsp", 2.0288413621105798E02, CDC_Volume ); // US teaspoon 1/768 gallon
2375 NEWD( "tbs", 6.7628045403685994E01, CDC_Volume ); // US tablespoon 1/256 gallon
2376 NEWD( "oz", 3.3814022701842997E01, CDC_Volume ); // Ounce Liquid 1/128 gallon
2377 NEWD( "cup", 4.2267528377303746E00, CDC_Volume ); // Cup 1/16 gallon
2378 NEWD( "pt", 2.1133764188651873E00, CDC_Volume ); // US Pint 1/8 gallon
2379 NEWD( "us_pt", 2.1133764188651873E00, CDC_Volume ); // US Pint also
2380 NEWD( "uk_pt", 1.7597539863927023E00, CDC_Volume ); // UK Pint 1/8 imperial gallon
2381 NEWD( "qt", 1.0566882094325937E00, CDC_Volume ); // Quart 1/4 gallon
2382 NEWD( "gal", 2.6417205235814842E-01, CDC_Volume ); // Gallon 1/3.785411784
2383 NEWDP( "l", 1.0000000000000000E00, CDC_Volume ); // Liter
2384 NEWDP( "L", 1.0000000000000000E00, CDC_Volume ); // Liter also
2385 NEWDP( "lt", 1.0000000000000000E00, CDC_Volume ); // Liter also
2386 NEWDP( "m3", 1.0000000000000000E-03, CDC_Volume ); // *** Cubic Meter
2387 NEWD( "mi3", 2.3991275857892772E-13, CDC_Volume ); // *** Cubic Britsh Mile
2388 NEWD( "Nmi3", 1.5742621468581148E-13, CDC_Volume ); // *** Cubic Nautical Mile
2389 NEWD( "in3", 6.1023744094732284E01, CDC_Volume ); // *** Cubic Inch
2390 NEWD( "ft3", 3.5314666721488590E-02, CDC_Volume ); // *** Cubic Foot
2391 NEWD( "yd3", 1.3079506193143922E-03, CDC_Volume ); // *** Cubic Yard
2392 NEWDP( "ang3", 1.0000000000000000E27, CDC_Volume ); // *** Cubic Angstroem
2393 NEWD( "Pica3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2394 NEWD( "picapt3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2395 NEWD( "pica3", 1.31811287245E04, CDC_Volume ); // *** Cubic Pica (1/6 inch)
2396 NEWD( "barrel", 6.2898107704321051E-03, CDC_Volume ); // *** Barrel (=42gal)
2397 NEWD( "bushel", 2.837759E-02, CDC_Volume ); // *** Bushel
2398 NEWD( "regton", 3.531467E-04, CDC_Volume ); // *** Register ton
2399 NEWD( "GRT", 3.531467E-04, CDC_Volume ); // *** Register ton also
2400 NEWD( "Schooner", 2.3529411764705882E00, CDC_Volume ); // *** austr. Schooner
2401 NEWD( "Middy", 3.5087719298245614E00, CDC_Volume ); // *** austr. Middy
2402 NEWD( "Glass", 5.0000000000000000E00, CDC_Volume ); // *** austr. Glass
2403 NEWD( "Sixpack", 0.5, CDC_Volume ); // ***
2404 NEWD( "Humpen", 2.0, CDC_Volume ); // ***
2405 NEWD( "ly3", 1.1810108125623799E-51, CDC_Volume ); // *** Cubic light-year
2406 NEWD( "MTON", 1.4125866688595436E00, CDC_Volume ); // *** Measurement ton
2407 NEWD( "tspm", 2.0000000000000000E02, CDC_Volume ); // *** Modern teaspoon
2408 NEWD( "uk_gal", 2.1996924829908779E-01, CDC_Volume ); // U.K. / Imperial gallon 1/4.54609
2409 NEWD( "uk_qt", 8.7987699319635115E-01, CDC_Volume ); // U.K. / Imperial quart 1/4 imperial gallon
2411 // 1 Square Meter is...
2412 NEWDP( "m2", 1.0000000000000000E00, CDC_Area ); // *** Square Meter
2413 NEWD( "mi2", 3.8610215854244585E-07, CDC_Area ); // *** Square Britsh Mile
2414 NEWD( "Nmi2", 2.9155334959812286E-07, CDC_Area ); // *** Square Nautical Mile
2415 NEWD( "in2", 1.5500031000062000E03, CDC_Area ); // *** Square Inch
2416 NEWD( "ft2", 1.0763910416709722E01, CDC_Area ); // *** Square Foot
2417 NEWD( "yd2", 1.1959900463010803E00, CDC_Area ); // *** Square Yard
2418 NEWDP( "ang2", 1.0000000000000000E20, CDC_Area ); // *** Square Angstroem
2419 NEWD( "Pica2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2420 NEWD( "picapt2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2421 NEWD( "pica2", 5.58001116002232E04, CDC_Area ); // *** Square Pica (1/6 inch)
2422 NEWD( "Morgen", 4.0000000000000000E-04, CDC_Area ); // *** Morgen
2423 NEWDP( "ar", 1.000000E-02, CDC_Area ); // *** Ar
2424 NEWD( "acre", 2.471053815E-04, CDC_Area ); // *** Acre
2425 NEWD( "uk_acre", 2.4710538146716534E-04, CDC_Area ); // *** International acre
2426 NEWD( "us_acre", 2.4710439304662790E-04, CDC_Area ); // *** U.S. survey/statute acre
2427 NEWD( "ly2", 1.1172985860549147E-32, CDC_Area ); // *** Square Light-year
2428 NEWD( "ha", 1.000000E-04, CDC_Area ); // *** Hectare
2430 // SPEED: 1 Meter per Second is...
2431 NEWDP( "m/s", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second
2432 NEWDP( "m/sec", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second also
2433 NEWDP( "m/h", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour
2434 NEWDP( "m/hr", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour also
2435 NEWD( "mph", 2.2369362920544023E00, CDC_Speed ); // *** Britsh Miles per Hour
2436 NEWD( "kn", 1.9438444924406048E00, CDC_Speed ); // *** Knot = Nautical Miles per Hour
2437 NEWD( "admkn", 1.9438446603753486E00, CDC_Speed ); // *** Admiralty Knot
2438 NEWD( "ludicrous speed", 2.0494886343432328E-14, CDC_Speed ); // ***
2439 NEWD( "ridiculous speed", 4.0156958471424288E-06, CDC_Speed); // ***
2441 // INFORMATION: 1 Bit is...
2442 NEWDP( "bit", 1.00E00, CDC_Information); // *** Bit
2443 NEWDP( "byte", 1.25E-01, CDC_Information); // *** Byte
2447 ConvertDataList::~ConvertDataList()
2452 double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo )
2454 ConvertData* pFrom = nullptr;
2455 ConvertData* pTo = nullptr;
2456 bool bSearchFrom = true;
2457 bool bSearchTo = true;
2458 sal_Int16 nLevelFrom = 0;
2459 sal_Int16 nLevelTo = 0;
2461 for( const auto& rItem : maVector )
2463 ConvertData* p = rItem.get();
2464 if( bSearchFrom )
2466 sal_Int16 n = p->GetMatchingLevel( rFrom );
2467 if( n != INV_MATCHLEV )
2469 if( n )
2470 { // only first match for partial equality rulz a little bit more
2471 pFrom = p;
2472 nLevelFrom = n;
2474 else
2475 { // ... but exact match rulz most
2476 pFrom = p;
2477 bSearchFrom = false;
2478 nLevelFrom = n;
2483 if( bSearchTo )
2485 sal_Int16 n = p->GetMatchingLevel( rTo );
2486 if( n != INV_MATCHLEV )
2488 if( n )
2489 { // only first match for partial equality rulz a little bit more
2490 pTo = p;
2491 nLevelTo = n;
2493 else
2494 { // ... but exact match rulz most
2495 pTo = p;
2496 bSearchTo = false;
2497 nLevelTo = n;
2502 if( !bSearchFrom && !bSearchTo )
2503 break;
2506 if( !pFrom || !pTo )
2507 throw lang::IllegalArgumentException();
2509 return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo );
2513 ScaDate::ScaDate() :
2514 nOrigDay( 1 ),
2515 nDay( 1 ),
2516 nMonth( 1 ),
2517 nYear( 1900 ),
2518 bLastDayMode( true ),
2519 bLastDay( false ),
2520 b30Days( false ),
2521 bUSMode( false )
2525 ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase )
2527 DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear );
2528 bLastDayMode = (nBase != 5);
2529 bLastDay = (nOrigDay >= ::DaysInMonth( nMonth, nYear ));
2530 b30Days = (nBase == 0) || (nBase == 4);
2531 bUSMode = (nBase == 0);
2532 setDay();
2535 ScaDate::ScaDate( const ScaDate& rCopy ) :
2536 nOrigDay( rCopy.nOrigDay ),
2537 nDay( rCopy.nDay ),
2538 nMonth( rCopy.nMonth ),
2539 nYear( rCopy.nYear ),
2540 bLastDayMode( rCopy.bLastDayMode ),
2541 bLastDay( rCopy.bLastDay ),
2542 b30Days( rCopy.b30Days ),
2543 bUSMode( rCopy.bUSMode )
2547 ScaDate& ScaDate::operator=( const ScaDate& rCopy )
2549 if( this != &rCopy )
2551 nOrigDay = rCopy.nOrigDay;
2552 nDay = rCopy.nDay;
2553 nMonth = rCopy.nMonth;
2554 nYear = rCopy.nYear;
2555 bLastDayMode = rCopy.bLastDayMode;
2556 bLastDay = rCopy.bLastDay;
2557 b30Days = rCopy.b30Days;
2558 bUSMode = rCopy.bUSMode;
2560 return *this;
2563 void ScaDate::setDay()
2565 if( b30Days )
2567 // 30-days-mode: set nDay to 30 if original was last day in month
2568 nDay = std::min( nOrigDay, static_cast< sal_uInt16 >( 30 ) );
2569 if( bLastDay || (nDay >= ::DaysInMonth( nMonth, nYear )) )
2570 nDay = 30;
2572 else
2574 // set nDay to last day in this month if original was last day
2575 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2576 nDay = bLastDay ? nLastDay : std::min( nOrigDay, nLastDay );
2580 sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2582 if( nFrom > nTo )
2583 return 0;
2585 sal_Int32 nRet = 0;
2586 if( b30Days )
2587 nRet = (nTo - nFrom + 1) * 30;
2588 else
2590 for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )
2591 nRet += getDaysInMonth( nMonthIx );
2593 return nRet;
2596 sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2598 if( nFrom > nTo )
2599 return 0;
2601 return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo );
2604 void ScaDate::doAddYears( sal_Int32 nYearCount )
2606 sal_Int32 nNewYear = nYearCount + nYear;
2607 if( (nNewYear < 0) || (nNewYear > 0x7FFF) )
2608 throw lang::IllegalArgumentException();
2609 nYear = static_cast< sal_uInt16 >( nNewYear );
2612 void ScaDate::addMonths( sal_Int32 nMonthCount )
2614 sal_Int32 nNewMonth = nMonthCount + nMonth;
2615 if( nNewMonth > 12 )
2617 --nNewMonth;
2618 doAddYears( nNewMonth / 12 );
2619 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1;
2621 else if( nNewMonth < 1 )
2623 doAddYears( nNewMonth / 12 - 1 );
2624 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 );
2626 else
2627 nMonth = static_cast< sal_uInt16 >( nNewMonth );
2628 setDay();
2631 sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const
2633 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2634 sal_uInt16 nRealDay = (bLastDayMode && bLastDay) ? nLastDay : std::min( nLastDay, nOrigDay );
2635 return ::DateToDays( nRealDay, nMonth, nYear ) - nNullDate;
2638 sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo )
2640 if( rFrom > rTo )
2641 return getDiff( rTo, rFrom );
2643 sal_Int32 nDiff = 0;
2644 ScaDate aFrom( rFrom );
2645 ScaDate aTo( rTo );
2647 if( rTo.b30Days )
2649 // corrections for base 0 (US NASD)
2650 if( rTo.bUSMode )
2652 if( ((rFrom.nMonth == 2) || (rFrom.nDay < 30)) && (aTo.nOrigDay == 31) )
2653 aTo.nDay = 31;
2654 else if( (aTo.nMonth == 2) && aTo.bLastDay )
2655 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2657 // corrections for base 4 (Europe)
2658 else
2660 if( (aFrom.nMonth == 2) && (aFrom.nDay == 30) )
2661 aFrom.nDay = ::DaysInMonth( 2, aFrom.nYear );
2662 if( (aTo.nMonth == 2) && (aTo.nDay == 30) )
2663 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2667 if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) )
2669 // move aFrom to 1st day of next month
2670 nDiff = aFrom.getDaysInMonth() - aFrom.nDay + 1;
2671 aFrom.nOrigDay = aFrom.nDay = 1;
2672 aFrom.bLastDay = false;
2673 aFrom.addMonths( 1 );
2675 if( aFrom.nYear < aTo.nYear )
2677 // move aFrom to 1st day of next year
2678 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 );
2679 aFrom.addMonths( 13 - aFrom.nMonth );
2681 // move aFrom to 1st day of this year
2682 nDiff += aFrom.getDaysInYearRange( aFrom.nYear, aTo.nYear - 1 );
2683 aFrom.addYears( aTo.nYear - aFrom.nYear );
2686 // move aFrom to 1st day of this month
2687 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, aTo.nMonth - 1 );
2688 aFrom.addMonths( aTo.nMonth - aFrom.nMonth );
2690 // finally add remaining days in this month
2691 nDiff += aTo.nDay - aFrom.nDay;
2692 return std::max<sal_Int32>(nDiff, 0);
2695 bool ScaDate::operator<( const ScaDate& rCmp ) const
2697 if( nYear != rCmp.nYear )
2698 return nYear < rCmp.nYear;
2699 if( nMonth != rCmp.nMonth )
2700 return nMonth < rCmp.nMonth;
2701 if( nDay != rCmp.nDay )
2702 return nDay < rCmp.nDay;
2703 if( bLastDay || rCmp.bLastDay )
2704 return !bLastDay && rCmp.bLastDay;
2705 return nOrigDay < rCmp.nOrigDay;
2709 ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext )
2710 : nDefaultFormat(0)
2711 , bHasValidFormat(false)
2713 xFormatter = util::NumberFormatter::create(xContext);
2716 ScaAnyConverter::~ScaAnyConverter()
2720 void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet )
2722 // try to get default number format
2723 bHasValidFormat = false;
2724 if( xFormatter.is() )
2726 // get XFormatsSupplier from outer XPropertySet
2727 uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY );
2728 if( xFormatsSupp.is() )
2730 // get XNumberFormatTypes from XNumberFormatsSupplier to get standard index
2731 uno::Reference< util::XNumberFormats > xFormats( xFormatsSupp->getNumberFormats() );
2732 uno::Reference< util::XNumberFormatTypes > xFormatTypes( xFormats, uno::UNO_QUERY );
2733 if( xFormatTypes.is() )
2735 lang::Locale eLocale;
2736 nDefaultFormat = xFormatTypes->getStandardIndex( eLocale );
2737 xFormatter->attachNumberFormatsSupplier( xFormatsSupp );
2738 bHasValidFormat = true;
2744 double ScaAnyConverter::convertToDouble( const OUString& rString ) const
2746 double fValue = 0.0;
2747 if( bHasValidFormat )
2751 fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString );
2753 catch( uno::Exception& )
2755 throw lang::IllegalArgumentException();
2758 else
2760 rtl_math_ConversionStatus eStatus;
2761 sal_Int32 nEnd;
2762 fValue = ::rtl::math::stringToDouble( rString, '.', ',', &eStatus, &nEnd );
2763 if( (eStatus != rtl_math_ConversionStatus_Ok) || (nEnd < rString.getLength()) )
2764 throw lang::IllegalArgumentException();
2766 return fValue;
2769 bool ScaAnyConverter::getDouble(
2770 double& rfResult,
2771 const uno::Any& rAny ) const
2773 rfResult = 0.0;
2774 bool bContainsVal = true;
2775 switch( rAny.getValueTypeClass() )
2777 case uno::TypeClass_VOID:
2778 bContainsVal = false;
2779 break;
2780 case uno::TypeClass_DOUBLE:
2781 rAny >>= rfResult;
2782 break;
2783 case uno::TypeClass_STRING:
2785 auto pString = o3tl::forceAccess< OUString >( rAny );
2786 if( !pString->isEmpty() )
2787 rfResult = convertToDouble( *pString );
2788 else
2789 bContainsVal = false;
2791 break;
2792 default:
2793 throw lang::IllegalArgumentException();
2795 return bContainsVal;
2798 bool ScaAnyConverter::getDouble(
2799 double& rfResult,
2800 const uno::Reference< beans::XPropertySet >& xPropSet,
2801 const uno::Any& rAny )
2803 init( xPropSet );
2804 return getDouble( rfResult, rAny );
2807 double ScaAnyConverter::getDouble(
2808 const uno::Reference< beans::XPropertySet >& xPropSet,
2809 const uno::Any& rAny,
2810 double fDefault )
2812 double fResult;
2813 if( !getDouble( fResult, xPropSet, rAny ) )
2814 fResult = fDefault;
2815 return fResult;
2818 bool ScaAnyConverter::getInt32(
2819 sal_Int32& rnResult,
2820 const uno::Reference< beans::XPropertySet >& xPropSet,
2821 const uno::Any& rAny )
2823 double fResult;
2824 bool bContainsVal = getDouble( fResult, xPropSet, rAny );
2825 if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) )
2826 throw lang::IllegalArgumentException();
2828 rnResult = static_cast< sal_Int32 >( fResult );
2829 return bContainsVal;
2832 sal_Int32 ScaAnyConverter::getInt32(
2833 const uno::Reference< beans::XPropertySet >& xPropSet,
2834 const uno::Any& rAny,
2835 sal_Int32 nDefault )
2837 sal_Int32 nResult;
2838 if( !getInt32( nResult, xPropSet, rAny ) )
2839 nResult = nDefault;
2840 return nResult;
2845 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */