diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /scaddins/source/analysis/analysishelper.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scaddins/source/analysis/analysishelper.cxx')
-rw-r--r-- | scaddins/source/analysis/analysishelper.cxx | 2813 |
1 files changed, 2813 insertions, 0 deletions
diff --git a/scaddins/source/analysis/analysishelper.cxx b/scaddins/source/analysis/analysishelper.cxx new file mode 100644 index 000000000..2e799c9d1 --- /dev/null +++ b/scaddins/source/analysis/analysishelper.cxx @@ -0,0 +1,2813 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> + +#include <string.h> +#include <stdio.h> +#include <o3tl/any.hxx> +#include <rtl/math.hxx> +#include <algorithm> +#include <memory> +#include "analysisdefs.hxx" +#include "analysishelper.hxx" +#include <analysis.hrc> +#include <strings.hrc> +#include "deffuncname.hxx" + +using namespace ::com::sun::star; +using namespace sca::analysis; + +#define UNIQUE false // function name does not exist in Calc +#define DOUBLE true // function name exists in Calc + +#define STDPAR false // all parameters are described +#define INTPAR true // first parameter is internal + +#define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \ + { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr } + +#define FUNCDATAS( FUNCNAME, DBL, OPT, NUMOFPAR, CAT, SUFFIX ) \ + { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, SUFFIX } + +const FuncDataBase pFuncDatas[] = +{ + // UNIQUE or INTPAR or + // function name DOUBLE STDPAR # of param category + FUNCDATA( Workday, UNIQUE, INTPAR, 3, FDCategory::DateTime ), + FUNCDATA( Yearfrac, UNIQUE, INTPAR, 3, FDCategory::DateTime ), + FUNCDATA( Edate, UNIQUE, INTPAR, 2, FDCategory::DateTime ), + FUNCDATAS( Weeknum, DOUBLE, INTPAR, 2, FDCategory::DateTime, "_EXCEL2003" ), + FUNCDATA( Eomonth, UNIQUE, INTPAR, 2, FDCategory::DateTime ), + FUNCDATAS( Networkdays, DOUBLE, INTPAR, 3, FDCategory::DateTime, "_EXCEL2003" ), + FUNCDATA( Iseven, DOUBLE, STDPAR, 1, FDCategory::Inf ), + FUNCDATA( Isodd, DOUBLE, STDPAR, 1, FDCategory::Inf ), + FUNCDATA( Multinomial, UNIQUE, STDPAR, 1, FDCategory::Math ), + FUNCDATA( Seriessum, UNIQUE, STDPAR, 4, FDCategory::Math ), + FUNCDATA( Quotient, UNIQUE, STDPAR, 2, FDCategory::Math ), + FUNCDATA( Mround, UNIQUE, STDPAR, 2, FDCategory::Math ), + FUNCDATA( Sqrtpi, UNIQUE, STDPAR, 1, FDCategory::Math ), + FUNCDATA( Randbetween, UNIQUE, STDPAR, 2, FDCategory::Math ), + FUNCDATAS( Gcd, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ), + FUNCDATAS( Lcm, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ), + FUNCDATA( Besseli, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Besselj, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Besselk, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Bessely, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Bin2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Bin2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Bin2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Oct2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Oct2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Oct2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Dec2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Dec2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Dec2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Hex2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Hex2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Hex2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Delta, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Erf, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Erfc, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Gestep, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Factdouble, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imabs, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imaginary, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Impower, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Imargument, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imcos, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imdiv, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Imexp, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imconjugate, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imln, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imlog10, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imlog2, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Improduct, UNIQUE, INTPAR, 2, FDCategory::Tech ), + FUNCDATA( Imreal, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsin, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsub, UNIQUE, STDPAR, 2, FDCategory::Tech ), + FUNCDATA( Imsqrt, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsum, UNIQUE, INTPAR, 1, FDCategory::Tech ), + FUNCDATA( Imtan, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsec, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imcsc, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imcot, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsinh, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imcosh, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imsech, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Imcsch, UNIQUE, STDPAR, 1, FDCategory::Tech ), + FUNCDATA( Complex, UNIQUE, STDPAR, 3, FDCategory::Tech ), + FUNCDATA( Convert, UNIQUE, STDPAR, 3, FDCategory::Tech ), + FUNCDATA( Amordegrc, UNIQUE, INTPAR, 7, FDCategory::Finance ), + FUNCDATA( Amorlinc, UNIQUE, INTPAR, 7, FDCategory::Finance ), + FUNCDATA( Accrint, UNIQUE, INTPAR, 7, FDCategory::Finance ), + FUNCDATA( Accrintm, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Received, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Disc, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Duration, UNIQUE, INTPAR, 6, FDCategory::Finance ), + FUNCDATA( Effect, DOUBLE, STDPAR, 2, FDCategory::Finance ), + FUNCDATA( Cumprinc, DOUBLE, STDPAR, 6, FDCategory::Finance ), + FUNCDATA( Cumipmt, DOUBLE, STDPAR, 6, FDCategory::Finance ), + FUNCDATA( Price, UNIQUE, INTPAR, 7, FDCategory::Finance ), + FUNCDATA( Pricedisc, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Pricemat, UNIQUE, INTPAR, 6, FDCategory::Finance ), + FUNCDATA( Mduration, UNIQUE, INTPAR, 6, FDCategory::Finance ), + FUNCDATA( Nominal, DOUBLE, STDPAR, 2, FDCategory::Finance ), + FUNCDATA( Dollarfr, UNIQUE, STDPAR, 2, FDCategory::Finance ), + FUNCDATA( Dollarde, UNIQUE, STDPAR, 2, FDCategory::Finance ), + FUNCDATA( Yield, UNIQUE, INTPAR, 7, FDCategory::Finance ), + FUNCDATA( Yielddisc, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Yieldmat, UNIQUE, INTPAR, 6, FDCategory::Finance ), + FUNCDATA( Tbilleq, UNIQUE, INTPAR, 3, FDCategory::Finance ), + FUNCDATA( Tbillprice, UNIQUE, INTPAR, 3, FDCategory::Finance ), + FUNCDATA( Tbillyield, UNIQUE, INTPAR, 3, FDCategory::Finance ), + FUNCDATA( Oddfprice, UNIQUE, INTPAR, 9, FDCategory::Finance ), + FUNCDATA( Oddfyield, UNIQUE, INTPAR, 9, FDCategory::Finance ), + FUNCDATA( Oddlprice, UNIQUE, INTPAR, 8, FDCategory::Finance ), + FUNCDATA( Oddlyield, UNIQUE, INTPAR, 8, FDCategory::Finance ), + FUNCDATA( Xirr, UNIQUE, INTPAR, 3, FDCategory::Finance ), + FUNCDATA( Xnpv, UNIQUE, STDPAR, 3, FDCategory::Finance ), + FUNCDATA( Intrate, UNIQUE, INTPAR, 5, FDCategory::Finance ), + FUNCDATA( Coupncd, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Coupdays, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Coupdaysnc, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Coupdaybs, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Couppcd, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Coupnum, UNIQUE, INTPAR, 4, FDCategory::Finance ), + FUNCDATA( Fvschedule, UNIQUE, STDPAR, 2, FDCategory::Finance ) +}; +#undef FUNCDATA + +namespace sca::analysis { + +sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear ) +{ + if( (nMonth == 2) && IsLeapYear( nYear ) ) + return 29; + static const sal_uInt16 aDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + return aDaysInMonth[ nMonth ]; +} + + +/** + * Convert a date to a count of days starting from 01/01/0001 + * + * The internal representation of a Date used in this Addin + * is the number of days between 01/01/0001 and the date + * this function converts a Day , Month, Year representation + * to this internal Date value. + * + */ + +sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear ) +{ + sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365; + nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400); + + for( sal_uInt16 i = 1; i < nMonth; i++ ) + nDays += DaysInMonth(i,nYear); + nDays += nDay; + + return nDays; +} + + +/** + * Convert a count of days starting from 01/01/0001 to a date + * + * The internal representation of a Date used in this Addin + * is the number of days between 01/01/0001 and the date + * this function converts this internal Date value + * to a Day , Month, Year representation of a Date. + * + */ + +void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear ) +{ + if( nDays < 0 ) + throw lang::IllegalArgumentException(); + + sal_Int32 nTempDays; + sal_Int32 i = 0; + bool bCalc; + + do + { + nTempDays = nDays; + rYear = static_cast<sal_uInt16>((nTempDays / 365) - i); + nTempDays -= (static_cast<sal_Int32>(rYear) -1) * 365; + nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400); + bCalc = false; + if ( nTempDays < 1 ) + { + i++; + bCalc = true; + } + else + { + if ( nTempDays > 365 ) + { + if ( (nTempDays != 366) || !IsLeapYear( rYear ) ) + { + i--; + bCalc = true; + } + } + } + } + while ( bCalc ); + + rMonth = 1; + while ( nTempDays > DaysInMonth( rMonth, rYear ) ) + { + nTempDays -= DaysInMonth( rMonth, rYear ); + rMonth++; + } + rDay = static_cast<sal_uInt16>(nTempDays); +} + + +/** + * Get the null date used by the spreadsheet document + * + * The internal representation of a Date used in this Addin + * is the number of days between 01/01/0001 and the date + * this function returns this internal Date value for the document null date + * + */ + +sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt ) +{ + if( xOpt.is() ) + { + try + { + uno::Any aAny = xOpt->getPropertyValue( "NullDate" ); + util::Date aDate; + if( aAny >>= aDate ) + return DateToDays( aDate.Day, aDate.Month, aDate.Year ); + } + catch( uno::Exception& ) + { + } + } + + // no null date available -> no calculations possible + throw uno::RuntimeException(); +} + + +sal_Int32 GetDiffDate360( + sal_uInt16 nDay1, sal_uInt16 nMonth1, sal_uInt16 nYear1, bool bLeapYear1, + sal_uInt16 nDay2, sal_uInt16 nMonth2, sal_uInt16 nYear2, + bool bUSAMethod ) +{ + if( nDay1 == 31 ) + nDay1--; + else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) ) + nDay1 = 30; + + if( nDay2 == 31 ) + { + if( bUSAMethod && nDay1 != 30 ) + { + nDay2 = 1; + if( nMonth2 == 12 ) + { + nYear2++; + nMonth2 = 1; + } + else + nMonth2++; + } + else + nDay2 = 30; + } + + return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360; +} + + +sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod ) +{ + nDate1 += nNullDate; + nDate2 += nNullDate; + + sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2; + + DaysToDate( nDate1, nDay1, nMonth1, nYear1 ); + DaysToDate( nDate2, nDay2, nMonth2, nYear2 ); + + return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod ); +} + + +sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 ) +{ + sal_uInt16 nLeaps = 0; + for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ ) + { + if( IsLeapYear( n ) ) + nLeaps++; + } + + sal_uInt32 nSum = 1; + nSum += nYear2; + nSum -= nYear1; + nSum *= 365; + nSum += nLeaps; + + return nSum; +} + + +sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode, + sal_Int32* pOptDaysIn1stYear ) +{ + bool bNeg = nStartDate > nEndDate; + + if( bNeg ) + { + sal_Int32 n = nEndDate; + nEndDate = nStartDate; + nStartDate = n; + } + + sal_Int32 nRet; + + switch( nMode ) + { + case 0: // 0=USA (NASD) 30/360 + case 4: // 4=Europe 30/360 + { + sal_uInt16 nD1, nM1, nY1, nD2, nM2, nY2; + + nStartDate += nNullDate; + nEndDate += nNullDate; + + DaysToDate( nStartDate, nD1, nM1, nY1 ); + DaysToDate( nEndDate, nD2, nM2, nY2 ); + + bool bLeap = IsLeapYear( nY1 ); + sal_Int32 nDays, nMonths; + + nMonths = nM2 - nM1; + nDays = nD2 - nD1; + + nMonths += ( nY2 - nY1 ) * 12; + + nRet = nMonths * 30 + nDays; + if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 ) + nRet -= bLeap? 1 : 2; + + if( pOptDaysIn1stYear ) + *pOptDaysIn1stYear = 360; + } + break; + case 1: // 1=exact/exact + if( pOptDaysIn1stYear ) + { + sal_uInt16 nD, nM, nY; + + DaysToDate( nStartDate + nNullDate, nD, nM, nY ); + + *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365; + } + nRet = nEndDate - nStartDate; + break; + case 2: // 2=exact/360 + nRet = nEndDate - nStartDate; + if( pOptDaysIn1stYear ) + *pOptDaysIn1stYear = 360; + break; + case 3: //3=exact/365 + nRet = nEndDate - nStartDate; + if( pOptDaysIn1stYear ) + *pOptDaysIn1stYear = 365; + break; + default: + throw lang::IllegalArgumentException(); + } + + return bNeg? -nRet : nRet; +} + + +double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ) +{ + sal_Int32 nDays1stYear; + sal_Int32 nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear ); + + return double( nTotalDays ) / double( nDays1stYear ); +} + + +sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode ) +{ + switch( nMode ) + { + case 0: // 0=USA (NASD) 30/360 + case 2: // 2=exact/360 + case 4: // 4=Europe 30/360 + return 360; + case 1: // 1=exact/exact + { + sal_uInt16 nD, nM, nY; + nDate += nNullDate; + DaysToDate( nDate, nD, nM, nY ); + return IsLeapYear( nY )? 366 : 365; + } + case 3: //3=exact/365 + return 365; + default: + throw lang::IllegalArgumentException(); + } +} + + +// tdf69569 making code compliant with change request for ODFF1.2 par 4.11.7.7 +/** + * Function GetYearFrac implements YEARFRAC as defined in: + * Open Document Format for Office Applications version 1.2 Part 2, par. 6.10.24 + * The calculations are defined in: + * Open Document Format for Office Applications version 1.2 Part 2, par. 4.11.7 + */ +double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ) +{ + if( nStartDate == nEndDate ) + return 0.0; // nothing to do... + + if( nStartDate > nEndDate ) + { + sal_Int32 n = nEndDate; + nEndDate = nStartDate; + nStartDate = n; + } + + sal_Int32 nDate1 = nStartDate + nNullDate; + sal_Int32 nDate2 = nEndDate + nNullDate; + + sal_uInt16 nDay1, nDay2; + sal_uInt16 nMonth1, nMonth2; + sal_uInt16 nYear1, nYear2; + + DaysToDate( nDate1, nDay1, nMonth1, nYear1 ); + DaysToDate( nDate2, nDay2, nMonth2, nYear2 ); + + // calculate days between nDate1 and nDate2 + sal_Int32 nDayDiff; + switch( nMode ) + { + case 0: // 0=USA (NASD) 30/360 + if ( nDay1 == 31 ) + { + nDay1--; + } + if ( nDay1 == 30 && nDay2 == 31 ) + { + nDay2--; + } + else + { + if ( nMonth1 == 2 && nDay1 == ( IsLeapYear( nYear1 ) ? 29 : 28 ) ) + { + nDay1 = 30; + if ( nMonth2 == 2 && nDay2 == ( IsLeapYear( nYear2 ) ? 29 : 28 ) ) + { + nDay2 = 30; + } + } + } + nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 ); + break; + case 1: // 1=exact/exact + case 2: // 2=exact/360 + case 3: // 3=exact/365 + nDayDiff = nDate2 - nDate1; + break; + case 4: // 4=Europe 30/360 + if ( nDay1 == 31 ) + { + nDay1--; + } + if ( nDay2 == 31 ) + { + nDay2--; + } + nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 ); + break; + default: + throw lang::IllegalArgumentException(); + } + + //calculate days in year + double nDaysInYear; + switch( nMode ) + { + case 0: // 0=USA (NASD) 30/360 + case 2: // 2=exact/360 + case 4: // 4=Europe 30/360 + nDaysInYear = 360; + break; + case 1: // 1=exact/exact + { + const bool isYearDifferent = ( nYear1 != nYear2 ); + // ODFv1.2 part 2 section 4.11.7.7.7 + if ( isYearDifferent && + ( ( nYear2 != nYear1 + 1 ) || + ( nMonth1 < nMonth2 ) || + ( nMonth1 == nMonth2 && nDay1 < nDay2 ) ) ) + { + // return average of days in year between nDate1 and nDate2, inclusive + sal_Int32 nDayCount = 0; + for ( sal_uInt16 i = nYear1; i <= nYear2; i++ ) + nDayCount += ( IsLeapYear( i ) ? 366 : 365 ); + + nDaysInYear = static_cast<double>(nDayCount) / static_cast<double>( nYear2 - nYear1 + 1 ); + } + else + { + // as a consequence, !isYearDifferent or + // nYear2 == nYear + 1 and (nMonth1 > nMonth2 or + // (nMonth1 == nMonth2 and nDay1 >= nDay2)) + assert( ( !isYearDifferent || + ( nYear1 + 1 == nYear2 && + ( nMonth1 > nMonth2 || + ( nMonth1 == nMonth2 || nDay1 >= nDay2 ) ) ) ) ); + + // ODFv1.2 part 2 section 4.11.7.7.8 (CHANGE REQUEST PENDING, see tdf6959) + if ( !isYearDifferent && IsLeapYear( nYear1 ) ) + { + nDaysInYear = 366; + } + else + { + // ODFv1.2 part 2 section 4.11.7.7.9/10 (CHANGE REQUEST PENDING, see tdf69569) + // we need to determine whether there is a 29 February + // between nDate1 (inclusive) and nDate2 (inclusive) + // the case of nYear1 == nYear2 is adequately tested in previous test + if( isYearDifferent && + ( ( IsLeapYear( nYear1 ) && + ( ( nMonth1 < 2 ) || ( ( nMonth1 == 2 ) && ( nDay1 <= 29 ) ) ) ) || + ( IsLeapYear( nYear2 ) && + ( nMonth2 > 2 || ( ( nMonth2 == 2 ) && ( nDay2 == 29 ) ) ) ) ) ) + { + nDaysInYear = 366; + } + else + { + nDaysInYear = 365; + } + } + } + } + break; + case 3: // 3=exact/365 + nDaysInYear = 365; + break; + // coverity[dead_error_begin] - following conditions exist to avoid compiler warning + default: + throw lang::IllegalArgumentException(); + } + + return double( nDayDiff ) / nDaysInYear; +} + +double BinomialCoefficient( double n, double k ) +{ + // This method is a copy of BinomKoeff() + // found in sc/source/core/tool/interpr3.cxx + + double nVal = 0.0; + k = ::rtl::math::approxFloor(k); + if (n < k) + nVal = 0.0; + else if (k == 0.0) + nVal = 1.0; + else + { + nVal = n/k; + n--; + k--; + while (k > 0.0) + { + nVal *= n/k; + k--; + n--; + } + } + return nVal; +} + +double GetGcd( double f1, double f2 ) +{ + double f = fmod( f1, f2 ); + while( f > 0.0 ) + { + f1 = f2; + f2 = f; + f = fmod( f1, f2 ); + } + + return f2; +} + + +double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim ) +{ + if ( nBase < 2 || nBase > 36 ) + throw lang::IllegalArgumentException(); + + sal_uInt32 nStrLen = aStr.getLength(); + if( nStrLen > nCharLim ) + throw lang::IllegalArgumentException(); + else if( !nStrLen ) + return 0.0; + + double fVal = 0.0; + + const sal_Unicode* p = aStr.getStr(); + + sal_uInt16 nFirstDig = 0; + bool bFirstDig = true; + double fBase = nBase; + + while ( *p ) + { + sal_uInt16 n; + + if( '0' <= *p && *p <= '9' ) + n = *p - '0'; + else if( 'A' <= *p && *p <= 'Z' ) + n = 10 + ( *p - 'A' ); + else if ( 'a' <= *p && *p <= 'z' ) + n = 10 + ( *p - 'a' ); + else + n = nBase; + + if( n >= nBase ) + throw lang::IllegalArgumentException(); // illegal char! + + if( bFirstDig ) + { + bFirstDig = false; + nFirstDig = n; + } + fVal = fVal * fBase + double( n ); + + p++; + + } + + if( nStrLen == nCharLim && !bFirstDig && (nFirstDig >= nBase / 2) ) + { // handling negative values + fVal = ( pow( double( nBase ), double( nCharLim ) ) - fVal ); // complement + fVal *= -1.0; + } + + return fVal; +} + + +static char GetMaxChar( sal_uInt16 nBase ) +{ + const char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + return c[ nBase ]; +} + + +OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase, + sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces ) +{ + fNum = ::rtl::math::approxFloor( fNum ); + fMin = ::rtl::math::approxFloor( fMin ); + fMax = ::rtl::math::approxFloor( fMax ); + + if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) ) + throw lang::IllegalArgumentException(); + + sal_Int64 nNum = static_cast< sal_Int64 >( fNum ); + bool bNeg = nNum < 0; + if( bNeg ) + nNum = sal_Int64( pow( double( nBase ), double( nMaxPlaces ) ) ) + nNum; + + OUString aRet(OUString::number(nNum, nBase).toAsciiUpperCase()); + + + if( bUsePlaces ) + { + sal_Int32 nLen = aRet.getLength(); + if( !bNeg && nLen > nPlaces ) + { + throw lang::IllegalArgumentException(); + } + else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) ) + { + sal_Int32 nLeft = nPlaces - nLen; + std::unique_ptr<char[]> p( new char[ nLeft + 1 ] ); + memset( p.get(), bNeg ? GetMaxChar( nBase ) : '0', nLeft ); + p[ nLeft ] = 0x00; + aRet = OUString( p.get(), nLeft, RTL_TEXTENCODING_MS_1252 ) + aRet; + } + } + + return aRet; +} + +// implementation moved to module sal, see #i97091# +double Erf( double x ) +{ + return ::rtl::math::erf(x); +} + +// implementation moved to module sal, see #i97091# +double Erfc( double x ) +{ + return ::rtl::math::erfc(x); +} + +static bool IsNum( sal_Unicode c ) +{ + return c >= '0' && c <= '9'; +} + + +static bool IsComma( sal_Unicode c ) +{ + return c == '.' || c == ','; +} + + +static bool IsExpStart( sal_Unicode c ) +{ + return c == 'e' || c == 'E'; +} + + +static bool IsImagUnit( sal_Unicode c ) +{ + return c == 'i' || c == 'j'; +} + + +static sal_uInt16 GetVal( sal_Unicode c ) +{ + return sal_uInt16( c - '0' ); +} + + +bool ParseDouble( const sal_Unicode*& rp, double& rRet ) +{ + double fInt = 0.0; + double fFrac = 0.0; + double fMult = 0.1; // multiplier to multiply digits with, when adding fractional ones + sal_Int32 nExp = 0; + sal_Int32 nMaxExp = 307; + sal_uInt16 nDigCnt = 18; // max. number of digits to read in, rest doesn't matter + + enum State { S_End = 0, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp }; + + State eS = S_Sign; + + bool bNegNum = false; + bool bNegExp = false; + + const sal_Unicode* p = rp; + sal_Unicode c; + + while( eS ) + { + c = *p; + switch( eS ) + { + case S_Sign: + if( IsNum( c ) ) + { + fInt = GetVal( c ); + nDigCnt--; + eS = S_Int; + } + else if( c == '-' ) + { + bNegNum = true; + eS = S_IntStart; + } + else if( c == '+' ) + eS = S_IntStart; + else if( IsComma( c ) ) + eS = S_Frac; + else + return false; + break; + case S_IntStart: + if( IsNum( c ) ) + { + fInt = GetVal( c ); + nDigCnt--; + eS = S_Int; + } + else if( IsComma( c ) ) + eS = S_Frac; + else if( IsImagUnit( c ) ) + { + rRet = 0.0; + return true; + } + else + return false; + break; + case S_Int: + if( IsNum( c ) ) + { + fInt *= 10.0; + fInt += double( GetVal( c ) ); + nDigCnt--; + if( !nDigCnt ) + eS = S_IgnoreIntDigs; + } + else if( IsComma( c ) ) + eS = S_Frac; + else if( IsExpStart( c ) ) + eS = S_ExpSign; + else + eS = S_End; + break; + case S_IgnoreIntDigs: + if( IsNum( c ) ) + nExp++; // just multiply num with 10... ;-) + else if( IsComma( c ) ) + eS = S_Frac; + else if( IsExpStart( c ) ) + eS = S_ExpSign; + else + eS = S_End; + break; + case S_Frac: + if( IsNum( c ) ) + { + fFrac += double( GetVal( c ) ) * fMult; + nDigCnt--; + if( nDigCnt ) + fMult *= 0.1; + else + eS = S_IgnoreFracDigs; + } + else if( IsExpStart( c ) ) + eS = S_ExpSign; + else + eS = S_End; + break; + case S_IgnoreFracDigs: + if( IsExpStart( c ) ) + eS = S_ExpSign; + else if( !IsNum( c ) ) + eS = S_End; + break; + case S_ExpSign: + if( IsNum( c ) ) + { + nExp = GetVal( c ); + eS = S_Exp; + } + else if( c == '-' ) + { + bNegExp = true; + eS = S_Exp; + } + else if( c != '+' ) + eS = S_End; + break; + case S_Exp: + if( IsNum( c ) ) + { + nExp *= 10; + nExp += GetVal( c ); + if( nExp > nMaxExp ) + return false; + } + else + eS = S_End; + break; + // coverity[dead_error_begin] - following conditions exist to avoid compiler warning + case S_End: + break; + } + + p++; + } + + p--; // set pointer back to last + rp = p; + + fInt += fFrac; + + if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error + { + sal_Int32 nLog10 = sal_Int32( log10( fInt ) ); + + if( bNegExp ) + nExp = -nExp; + + if( nLog10 + nExp > nMaxExp ) + return false; + + fInt = ::rtl::math::pow10Exp( fInt, nExp ); + } + + if( bNegNum ) + fInt = -fInt; + + rRet = fInt; + + return true; +} + + +OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig ) +{ + const int nBuff = 256; + char aBuff[ nBuff + 1 ]; + const char* pFormStr = bLeadingSign? "%+.*g" : "%.*g"; + int nLen = snprintf( aBuff, nBuff, pFormStr, int( nMaxDig ), f ); + // you never know which underlying implementation you get ... + aBuff[nBuff] = 0; + if ( nLen < 0 || nLen > nBuff ) + nLen = strlen( aBuff ); + + OUString aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 ); + + return aRet; +} + + +double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, + double fRestVal, double fPer, double fRate, sal_Int32 nBase ) +{ + sal_uInt32 nPer = sal_uInt32( fPer ); + double fUsePer = 1.0 / fRate; + double fAmorCoeff; + + if( fUsePer < 3.0 ) + fAmorCoeff = 1.0; + else if( fUsePer < 5.0 ) + fAmorCoeff = 1.5; + else if( fUsePer <= 6.0 ) + fAmorCoeff = 2.0; + else + fAmorCoeff = 2.5; + + fRate *= fAmorCoeff; + double fNRate = ::rtl::math::round( GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost ); + fCost -= fNRate; + double fRest = fCost - fRestVal; // aboriginal cost - residual value - sum of all write-downs + + for( sal_uInt32 n = 0 ; n < nPer ; n++ ) + { + fNRate = ::rtl::math::round( fRate * fCost ); + fRest -= fNRate; + + if( fRest < 0.0 ) + { + switch( nPer - n ) + { + case 0: + case 1: + return ::rtl::math::round( fCost * 0.5 ); + default: + return 0.0; + } + } + + fCost -= fNRate; + } + + return fNRate; +} + + +double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, + double fRestVal, double fPer, double fRate, sal_Int32 nBase ) +{ + sal_uInt32 nPer = sal_uInt32( fPer ); + double fOneRate = fCost * fRate; + double fCostDelta = fCost - fRestVal; + double f0Rate = GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost; + sal_uInt32 nNumOfFullPeriods = sal_uInt32( ( fCost - fRestVal - f0Rate) / fOneRate ); + + double fResult = 0.0; + if( nPer == 0 ) + fResult = f0Rate; + else if( nPer <= nNumOfFullPeriods ) + fResult = fOneRate; + else if( nPer == nNumOfFullPeriods + 1 ) + fResult = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate; + + if ( fResult > 0.0 ) + return fResult; + else + return 0.0; +} + + +double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, + double fYield, sal_Int32 nFreq, sal_Int32 nBase ) +{ + double fYearfrac = GetYearFrac( nNullDate, nSettle, nMat, nBase ); + double fNumOfCoups = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase ); + double fDur = 0.0; + const double f100 = 100.0; + fCoup *= f100 / double( nFreq ); // fCoup is used as cash flow + fYield /= nFreq; + fYield += 1.0; + + double nDiff = fYearfrac * nFreq - fNumOfCoups; + + double t; + + for( t = 1.0 ; t < fNumOfCoups ; t++ ) + fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff ); + + fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff ); + + double p = 0.0; + for( t = 1.0 ; t < fNumOfCoups ; t++ ) + p += fCoup / pow( fYield, t + nDiff ); + + p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff ); + + fDur /= p; + fDur /= double( nFreq ); + + return fDur; +} + + +double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, + double fRate, double fPrice, sal_Int32 nBase ) +{ + double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase ); + double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase ); + double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase ); + + double y = 1.0 + fIssMat * fRate; + y /= fPrice / 100.0 + fIssSet * fRate; + y--; + y /= fSetMat; + + return y; +} + + +double GetOddfprice( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/, + sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fYield*/, double /*fRedemp*/, sal_Int32 /*nFreq*/, + sal_Int32 /*nBase*/ ) +{ + // If you change this to not unconditionally throw, the + // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in + // financial.cxx can be removed. + throw uno::RuntimeException(); +} + + +double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice, + double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ) +{ + double fRate = fCoup; + double fPriceN = 0.0; + double fYield1 = 0.0; + double fYield2 = 1.0; + double fPrice1 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield1, fRedemp, nFreq, nBase ); + double fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase ); + double fYieldN = ( fYield2 - fYield1 ) * 0.5; + + for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ ) + { + fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase ); + + if( rtl::math::approxEqual(fPrice, fPrice1) ) + return fYield1; + else if( rtl::math::approxEqual(fPrice, fPrice2) ) + return fYield2; + else if( rtl::math::approxEqual(fPrice, fPriceN) ) + return fYieldN; + else if( fPrice < fPrice2 ) + { + fYield2 *= 2.0; + fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase ); + + fYieldN = ( fYield2 - fYield1 ) * 0.5; + } + else + { + if( fPrice < fPriceN ) + { + fYield1 = fYieldN; + fPrice1 = fPriceN; + } + else + { + fYield2 = fYieldN; + fPrice2 = fPriceN; + } + + fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) ); + } + } + + if( fabs( fPrice - fPriceN ) > fPrice / 100.0 ) + throw lang::IllegalArgumentException(); // result not precise enough + + return fYieldN; +} + + +double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield, + double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ) +{ + double fFreq = nFreq; + + double fE = GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ); + double fDSC_E = GetCoupdaysnc( nNullDate, nSettle, nMat, nFreq, nBase ) / fE; + double fN = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase ); + double fA = GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase ); + + double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) ); + fRet -= 100.0 * fRate / fFreq * fA / fE; + + double fT1 = 100.0 * fRate / fFreq; + double fT2 = 1.0 + fYield / fFreq; + + for( double fK = 0.0 ; fK < fN ; fK++ ) + fRet += fT1 / pow( fT2, fK + fDSC_E ); + + return fRet; +} + + +double GetOddfyield( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/, + sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fPrice*/, double /*fRedemp*/, sal_Int32 /*nFreq*/, + sal_Int32 /*nBase*/ ) +{ + // If you change this to not unconditionally throw, the + // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in + // financial.cxx can be removed. + throw uno::RuntimeException(); +} + + +double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup, + double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ) +{ + double fFreq = double( nFreq ); + double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq; + double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq; + double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq; + + double p = fRedemp + fDCi * 100.0 * fRate / fFreq; + p /= fDSCi * fYield / fFreq + 1.0; + p -= fAi * 100.0 * fRate / fFreq; + + return p; +} + + +double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup, + double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ) +{ + double fFreq = double( nFreq ); + double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq; + double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq; + double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq; + + double y = fRedemp + fDCi * 100.0 * fRate / fFreq; + y /= fPrice + fAi * 100.0 * fRate / fFreq; + y--; + y *= fFreq / fDSCi; + + return y; +} + + +double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType ) +{ + double fPmt; + if( fRate == 0.0 ) + fPmt = ( fPv + fFv ) / fNper; + else + { + double fTerm = pow( 1.0 + fRate, fNper ); + if( nPayType > 0 ) + fPmt = ( fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fRate ); + else + fPmt = fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ); + } + + return -fPmt; +} + + +double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType ) +{ + double fFv; + if( fRate == 0.0 ) + fFv = fPv + fPmt * fNper; + else + { + double fTerm = pow( 1.0 + fRate, fNper ); + if( nPayType > 0 ) + fFv = fPv * fTerm + fPmt * ( 1.0 + fRate ) * ( fTerm - 1.0 ) / fRate; + else + fFv = fPv * fTerm + fPmt * ( fTerm - 1.0 ) / fRate; + } + + return -fFv; +} + +// financial functions COUP*** + +// COUPPCD: find last coupon date before settlement (can be equal to settlement) +/// @throws css::lang::IllegalArgumentException +static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq ) +{ + rDate = rMat; + rDate.setYear( rSettle.getYear() ); + if( rDate < rSettle ) + rDate.addYears( 1 ); + while( rDate > rSettle ) + rDate.addMonths( -12 / nFreq ); +} + +double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + ScaDate aDate; + lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq ); + return aDate.getDate( nNullDate ); +} + +// COUPNCD: find first coupon date after settlement (is never equal to settlement) +/// @throws css::lang::IllegalArgumentException +static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq ) +{ + rDate = rMat; + rDate.setYear( rSettle.getYear() ); + if( rDate > rSettle ) + rDate.addYears( -1 ); + while( rDate <= rSettle ) + rDate.addMonths( 12 / nFreq ); +} + +double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + ScaDate aDate; + lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq ); + return aDate.getDate( nNullDate ); +} + +// COUPDAYBS: get day count: coupon date before settlement <-> settlement +double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + ScaDate aSettle( nNullDate, nSettle, nBase ); + ScaDate aDate; + lcl_GetCouppcd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq ); + return ScaDate::getDiff( aDate, aSettle ); +} + +// COUPDAYSNC: get day count: settlement <-> coupon date after settlement +double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + if( (nBase != 0) && (nBase != 4) ) + { + ScaDate aSettle( nNullDate, nSettle, nBase ); + ScaDate aDate; + lcl_GetCoupncd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq ); + return ScaDate::getDiff( aSettle, aDate ); + } + return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase ); +} + +// COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement +double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + if( nBase == 1 ) + { + ScaDate aDate; + lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq ); + ScaDate aNextDate( aDate ); + aNextDate.addMonths( 12 / nFreq ); + return ScaDate::getDiff( aDate, aNextDate ); + } + return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq; +} + +// COUPNUM: get count of coupon dates +double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase ) +{ + if( nSettle >= nMat || CHK_Freq ) + throw lang::IllegalArgumentException(); + + ScaDate aMat( nNullDate, nMat, nBase ); + ScaDate aDate; + lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), aMat, nFreq ); + sal_uInt16 nMonths = (aMat.getYear() - aDate.getYear()) * 12 + aMat.getMonth() - aDate.getMonth(); + return static_cast< double >( nMonths * nFreq / 12 ); +} + +FuncData::FuncData(const FuncDataBase& r) : + aIntName( OUString::createFromAscii( r.pIntName ) ), + pUINameID( r.pUINameID ), + pDescrID( r.pDescrID ), + bDouble( r.bDouble ), + bWithOpt( r.bWithOpt ), + nParam( r.nNumOfParams ), + eCat( r.eCat ) +{ + if (r.pSuffix) + aSuffix = OUString::createFromAscii(r.pSuffix); + + aCompList.resize(2); + aCompList[0] = OUString(r.pCompListID[0], strlen(r.pCompListID[0]), RTL_TEXTENCODING_UTF8); + aCompList[1] = OUString(r.pCompListID[1], strlen(r.pCompListID[1]), RTL_TEXTENCODING_UTF8); +} + +sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const +{ + if( !bWithOpt ) + nParamNum++; + + if( nParamNum > nParam ) + return nParam * 2; + else + return nParamNum * 2; +} + +void InitFuncDataList(FuncDataList& rList) +{ + for(const auto & rFuncData : pFuncDatas) + rList.push_back(FuncData(rFuncData)); +} + +SortedIndividualInt32List::SortedIndividualInt32List() +{ +} + + +SortedIndividualInt32List::~SortedIndividualInt32List() +{ +} + + +void SortedIndividualInt32List::Insert( sal_Int32 nDay ) +{ + sal_uInt32 nIndex = Count(); + while( nIndex ) + { + nIndex--; + sal_Int32 nRef = Get( nIndex ); + if( nDay == nRef ) + return; + else if( nDay > nRef ) + { + maVector.insert( maVector.begin() + nIndex + 1, nDay ); + return; + } + } + maVector.insert( maVector.begin(), nDay ); +} + + +void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend ) +{ + if( !nDay ) + return; + + nDay += nNullDate; + if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) ) + Insert( nDay ); +} + + +void SortedIndividualInt32List::Insert( + double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend ) +{ + if( (fDay < -2147483648.0) || (fDay > 2147483649.0) ) + throw lang::IllegalArgumentException(); + Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend ); +} + + +bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const +{ + sal_uInt32 nE = Count(); + + if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) ) + return false; + + // linear search + + for( sal_uInt32 n = 0 ; n < nE ; n++ ) + { + sal_Int32 nRef = Get( n ); + + if( nRef == nVal ) + return true; + else if( nRef > nVal ) + return false; + } + return false; +} + + +void SortedIndividualInt32List::InsertHolidayList( + const ScaAnyConverter& rAnyConv, + const uno::Any& rHolAny, + sal_Int32 nNullDate, + bool bInsertOnWeekend ) +{ + double fDay; + if( rAnyConv.getDouble( fDay, rHolAny ) ) + Insert( fDay, nNullDate, bInsertOnWeekend ); +} + + +void SortedIndividualInt32List::InsertHolidayList( + ScaAnyConverter& rAnyConv, + const uno::Reference< beans::XPropertySet >& xOptions, + const uno::Any& rHolAny, + sal_Int32 nNullDate ) +{ + rAnyConv.init( xOptions ); + if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE ) + { + uno::Sequence< uno::Sequence< uno::Any > > aAnySeq; + if( !(rHolAny >>= aAnySeq) ) + throw lang::IllegalArgumentException(); + + for( const uno::Sequence< uno::Any >& rSubSeq : std::as_const(aAnySeq) ) + { + for( const uno::Any& rAny : rSubSeq ) + InsertHolidayList( rAnyConv, rAny, nNullDate, false/*bInsertOnWeekend*/ ); + } + } + else + InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ ); +} + + +void ScaDoubleList::Append( + const uno::Sequence< uno::Sequence< double > >& rValueSeq ) +{ + for( const uno::Sequence< double >& rSubSeq : rValueSeq ) + { + for( const double fValue : rSubSeq ) + Append( fValue ); + } +} + + +void ScaDoubleList::Append( + const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq ) +{ + for( const uno::Sequence< sal_Int32 >& rSubSeq : rValueSeq ) + { + for( const sal_Int32 nValue : rSubSeq ) + Append( nValue ); + } +} + +void ScaDoubleList::Append( + const ScaAnyConverter& rAnyConv, + const uno::Any& rAny, + bool bIgnoreEmpty ) +{ + if( auto s = o3tl::tryAccess< + css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) ) + Append( rAnyConv, *s, bIgnoreEmpty ); + else + { + double fValue; + if( rAnyConv.getDouble( fValue, rAny ) ) + Append( fValue ); + else if( !bIgnoreEmpty ) + Append( 0.0 ); + } +} + + +void ScaDoubleList::Append( + const ScaAnyConverter& rAnyConv, + const uno::Sequence< uno::Any >& rAnySeq, + bool bIgnoreEmpty ) +{ + for( const uno::Any& rAny : rAnySeq ) + Append( rAnyConv, rAny, bIgnoreEmpty ); +} + + +void ScaDoubleList::Append( + const ScaAnyConverter& rAnyConv, + const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq, + bool bIgnoreEmpty ) +{ + for( const uno::Sequence< uno::Any >& rArray : rAnySeq ) + Append( rAnyConv, rArray, bIgnoreEmpty ); +} + +void ScaDoubleList::Append( + ScaAnyConverter& rAnyConv, + const uno::Reference< beans::XPropertySet >& xOpt, + const uno::Sequence< uno::Any >& rAnySeq ) +{ + rAnyConv.init( xOpt ); + Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ ); +} + + +bool ScaDoubleList::CheckInsert( double ) const +{ + return true; +} + + +bool ScaDoubleListGT0::CheckInsert( double fValue ) const +{ + if( fValue < 0.0 ) + throw lang::IllegalArgumentException(); + return fValue > 0.0; +} + + +bool ScaDoubleListGE0::CheckInsert( double fValue ) const +{ + if( fValue < 0.0 ) + throw lang::IllegalArgumentException(); + return true; +} + + +Complex::Complex( const OUString& rStr ) +{ + if( !ParseString( rStr, *this ) ) + throw lang::IllegalArgumentException(); +} + + +inline bool Complex::IsImagUnit( sal_Unicode c ) +{ + return c == 'i' || c == 'j'; +} + +bool Complex::ParseString( const OUString& rStr, Complex& rCompl ) +{ + rCompl.c = '\0'; // do not force a symbol, if only real part present + + const sal_Unicode* pStr = rStr.getStr(); + + if( IsImagUnit( *pStr ) && rStr.getLength() == 1) + { + rCompl.r = 0.0; + rCompl.i = 1.0; + rCompl.c = *pStr; + return true; + } + + double f; + + if( !ParseDouble( pStr, f ) ) + return false; + + switch( *pStr ) + { + case '-': // imag part follows + case '+': + { + double r = f; + if( IsImagUnit( pStr[ 1 ] ) ) + { + rCompl.c = pStr[ 1 ]; + if( pStr[ 2 ] == 0 ) + { + rCompl.r = f; + rCompl.i = ( *pStr == '+' )? 1.0 : -1.0; + return true; + } + } + else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) ) + { + rCompl.c = *pStr; + pStr++; + if( *pStr == 0 ) + { + rCompl.r = r; + rCompl.i = f; + return true; + } + } + } + break; + case 'j': + case 'i': + rCompl.c = *pStr; + pStr++; + if( *pStr == 0 ) + { + rCompl.i = f; + rCompl.r = 0.0; + return true; + } + break; + case 0: // only real-part + rCompl.r = f; + rCompl.i = 0.0; + return true; + } + + return false; +} + + +OUString Complex::GetString() const +{ + CHK_FINITE(r); + CHK_FINITE(i); + OUStringBuffer aRet; + + bool bHasImag = i != 0.0; + bool bHasReal = !bHasImag || (r != 0.0); + + if( bHasReal ) + aRet.append(::GetString( r, false )); + if( bHasImag ) + { + if( i == 1.0 ) + { + if( bHasReal ) + aRet.append('+'); + } + else if( i == -1.0 ) + aRet.append('-'); + else + aRet.append(::GetString( i, bHasReal )); + aRet.append((c != 'j') ? 'i' : 'j'); + } + + return aRet.makeStringAndClear(); +} + + +double Complex::Arg() const +{ + if( r == 0.0 && i == 0.0 ) + throw lang::IllegalArgumentException(); + + double phi = acos( r / Abs() ); + + if( i < 0.0 ) + phi = -phi; + + return phi; +} + + +void Complex::Power( double fPower ) +{ + if( r == 0.0 && i == 0.0 ) + { + if( fPower <= 0 ) + throw lang::IllegalArgumentException(); + r = i = 0.0; + return; + } + + double p, phi; + + p = Abs(); + + phi = acos( r / p ); + if( i < 0.0 ) + phi = -phi; + + p = pow( p, fPower ); + phi *= fPower; + + r = cos( phi ) * p; + i = sin( phi ) * p; +} + + +void Complex::Sqrt() +{ + static const double fMultConst = 0.7071067811865475; // ...2440084436210485 = 1/sqrt(2) + double p = Abs(); + double i_ = sqrt( p - r ) * fMultConst; + + r = sqrt( p + r ) * fMultConst; + i = ( i < 0.0 )? -i_ : i_; +} + + +void Complex::Sin() +{ + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + + if( i ) + { + double r_; + + r_ = sin( r ) * cosh( i ); + i = cos( r ) * sinh( i ); + r = r_; + } + else + r = sin( r ); +} + + +void Complex::Cos() +{ + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + + if( i ) + { + double r_; + + r_ = cos( r ) * cosh( i ); + i = -( sin( r ) * sinh( i ) ); + r = r_; + } + else + r = cos( r ); +} + + +void Complex::Div( const Complex& z ) +{ + if( z.r == 0 && z.i == 0 ) + throw lang::IllegalArgumentException(); + + double a1 = r; + double a2 = z.r; + double b1 = i; + double b2 = z.i; + + double f = 1.0 / ( a2 * a2 + b2 * b2 ); + + r = ( a1 * a2 + b1 * b2 ) * f; + i = ( a2 * b1 - a1 * b2 ) * f; + + if( !c ) c = z.c; +} + + +void Complex::Exp() +{ + double fE = exp( r ); + r = fE * cos( i ); + i = fE * sin( i ); +} + + +void Complex::Ln() +{ + if( r == 0.0 && i == 0.0 ) + throw lang::IllegalArgumentException(); + + double fAbs = Abs(); + bool bNegi = i < 0.0; + + i = acos( r / fAbs ); + + if( bNegi ) + i = -i; + + r = log( fAbs ); +} + + +void Complex::Log10() +{ + Ln(); + Mult( 0.434294481903251828 ); // * log10( e ) +} + + +void Complex::Log2() +{ + Ln(); + Mult( 1.442695040888963407 ); // * log2( e ) +} + + +void Complex::Tan() +{ + if ( i ) + { + if( !::rtl::math::isValidArcArg( 2.0 * r ) ) + throw lang::IllegalArgumentException(); + double fScale =1.0 / ( cos( 2.0 * r ) + cosh( 2.0 * i )); + r = sin( 2.0 * r ) * fScale; + i = sinh( 2.0 * i ) * fScale; + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = tan( r ); + } +} + + +void Complex::Sec() +{ + if( i ) + { + if( !::rtl::math::isValidArcArg( 2 * r ) ) + throw lang::IllegalArgumentException(); + double fScale = 1.0 / (cosh( 2.0 * i) + cos ( 2.0 * r)); + double r_; + r_ = 2.0 * cos( r ) * cosh( i ) * fScale; + i = 2.0 * sin( r ) * sinh( i ) * fScale; + r = r_; + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = 1.0 / cos( r ); + } +} + + +void Complex::Csc() +{ + if( i ) + { + if( !::rtl::math::isValidArcArg( 2 * r ) ) + throw lang::IllegalArgumentException(); + double fScale = 1.0 / (cosh( 2.0 * i) - cos ( 2.0 * r)); + double r_; + r_ = 2.0 * sin( r ) * cosh( i ) * fScale; + i = -2.0 * cos( r ) * sinh( i ) * fScale; + r = r_; + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = 1.0 / sin( r ); + } +} + + +void Complex::Cot() +{ + if ( i ) + { + if( !::rtl::math::isValidArcArg( 2.0 * r ) ) + throw lang::IllegalArgumentException(); + double fScale =1.0 / ( cosh( 2.0 * i ) - cos( 2.0 * r ) ); + r = sin( 2.0 * r ) * fScale; + i = - ( sinh( 2.0 * i ) * fScale ); + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = 1.0 / tan( r ); + } +} + + +void Complex::Sinh() +{ + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + + if( i ) + { + double r_; + r_ = sinh( r ) * cos( i ); + i = cosh( r ) * sin( i ); + r = r_; + } + else + r = sinh( r ); +} + + +void Complex::Cosh() +{ + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + + if( i ) + { + double r_; + r_ = cosh( r ) * cos( i ); + i = sinh( r ) * sin( i ); + r = r_; + } + else + r = cosh( r ); +} + + +void Complex::Sech() +{ + if ( i ) + { + if( !::rtl::math::isValidArcArg( 2.0 * r ) ) + throw lang::IllegalArgumentException(); + double fScale =1.0 / ( cosh( 2.0 * r ) + cos( 2.0 * i )); + double r_; + r_ = 2.0 * cosh( r ) * cos( i ) * fScale; + i = - (2.0 * sinh( r ) * sin( i ) * fScale ); + r = r_ ; + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = 1.0 / cosh( r ); + } +} + + +void Complex::Csch() +{ + if ( i ) + { + if( !::rtl::math::isValidArcArg( 2.0 * r ) ) + throw lang::IllegalArgumentException(); + double fScale =1.0 / ( cosh( 2.0 * r ) - cos( 2.0 * i )); + double r_; + r_ = 2.0 * sinh( r ) * cos( i ) * fScale; + i = - ( 2.0 * cosh( r ) * sin( i ) * fScale ); + r = r_ ; + } + else + { + if( !::rtl::math::isValidArcArg( r ) ) + throw lang::IllegalArgumentException(); + r = 1.0 / sinh( r ); + } +} + + +ComplexList::~ComplexList() +{ +} + + +void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r ) +{ + for( const uno::Sequence< OUString >& rList : r ) + { + for( const OUString& rStr : rList ) + { + if( !rStr.isEmpty() ) + Append( Complex( rStr ) ); + } + } +} + + +void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars ) +{ + for( const uno::Any& r : aMultPars ) + { + switch( r.getValueTypeClass() ) + { + case uno::TypeClass_VOID: break; + case uno::TypeClass_STRING: + { + auto pStr = o3tl::forceAccess<OUString>(r); + + if( !pStr->isEmpty() ) + Append( Complex( *pStr ) ); + } + break; + case uno::TypeClass_DOUBLE: + Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) ); + break; + case uno::TypeClass_SEQUENCE: + { + uno::Sequence< uno::Sequence< uno::Any > > aValArr; + if( !(r >>= aValArr) ) + throw lang::IllegalArgumentException(); + + for( const uno::Sequence< uno::Any >& rArr : std::as_const(aValArr) ) + Append( rArr ); + } + break; + default: + throw lang::IllegalArgumentException(); + } + } +} + +ConvertData::ConvertData(const char p[], double fC, ConvertDataClass e, bool bPrefSupport) + : fConst(fC) + , aName(p, strlen(p), RTL_TEXTENCODING_MS_1252) + , eClass(e) + , bPrefixSupport(bPrefSupport) +{ +} + +ConvertData::~ConvertData() +{ +} + +sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const +{ + OUString aStr = rRef; + sal_Int32 nLen = rRef.getLength(); + sal_Int32 nIndex = rRef.lastIndexOf( '^' ); + if( nIndex > 0 && nIndex == ( nLen - 2 ) ) + aStr = aStr.copy( 0, nLen - 2 ) + OUStringChar( aStr[ nLen - 1 ] ); + if( aName == aStr ) + return 0; + else + { + const sal_Unicode* p = aStr.getStr(); + + nLen = aStr.getLength(); + bool bPref = bPrefixSupport; + bool bOneChar = (bPref && nLen > 1 && (aName == p + 1)); + if (bOneChar || (bPref && nLen > 2 && (aName == p + 2) && + *p == 'd' && *(p+1) == 'a')) + { + sal_Int16 n; + switch( *p ) + { + case 'y': n = -24; break; // yocto + case 'z': n = -21; break; // zepto + case 'a': n = -18; break; + case 'f': n = -15; break; + case 'p': n = -12; break; + case 'n': n = -9; break; + case 'u': n = -6; break; + case 'm': n = -3; break; + case 'c': n = -2; break; + case 'd': + { + if ( bOneChar ) + n = -1; // deci + else + n = 1; // deca + } + break; + case 'e': n = 1; break; + case 'h': n = 2; break; + case 'k': n = 3; break; + case 'M': n = 6; break; + case 'G': n = 9; break; + case 'T': n = 12; break; + case 'P': n = 15; break; + case 'E': n = 18; break; + case 'Z': n = 21; break; // zetta + case 'Y': n = 24; break; // yotta + default: + n = INV_MATCHLEV; + } + +// We could weed some nonsense out, ODFF doesn't say so though. +#if 0 + if (n < 0 && Class() == CDC_Information) + n = INV_MATCHLEV; // milli-bits doesn't make sense +#endif + +//! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------ + if( n != INV_MATCHLEV ) + { + sal_Unicode cLast = p[ aStr.getLength() - 1 ]; + if( cLast == '2' ) + n *= 2; + else if( cLast == '3' ) + n *= 3; + } +//! </HACK> ------------------------------------------------------------------- + + return n; + } + else if ( nLen > 2 && ( aName == p + 2 ) && ( Class() == CDC_Information ) ) + { + const sal_Unicode* pStr = aStr.getStr(); + if ( *(pStr + 1) != 'i') + return INV_MATCHLEV; + sal_Int16 n; + switch( *pStr ) + { + case 'k': n = 10; break; + case 'M': n = 20; break; + case 'G': n = 30; break; + case 'T': n = 40; break; + case 'P': n = 50; break; + case 'E': n = 60; break; + case 'Z': n = 70; break; + case 'Y': n = 80; break; + default: + n = INV_MATCHLEV; + } + return n; + } + else + return INV_MATCHLEV; + } +} + + +double ConvertData::Convert( + double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const +{ + if( Class() != r.Class() ) + throw lang::IllegalArgumentException(); + + bool bBinFromLev = ( nLevFrom > 0 && ( nLevFrom % 10 ) == 0 ); + bool bBinToLev = ( nLevTo > 0 && ( nLevTo % 10 ) == 0 ); + + if ( Class() == CDC_Information && ( bBinFromLev || bBinToLev ) ) + { + if ( bBinFromLev && bBinToLev ) + { + nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo ); + f *= r.fConst / fConst; + if( nLevFrom ) + f *= pow( 2.0, nLevFrom ); + } + else if ( bBinFromLev ) + f *= ( r.fConst / fConst ) * ( pow( 2.0, nLevFrom ) / pow( 10.0, nLevTo ) ); + else + f *= ( r.fConst / fConst ) * ( pow( 10.0, nLevFrom ) / pow( 2.0, nLevTo ) ); + return f; + } + + nLevFrom = sal::static_int_cast<sal_Int16>( nLevFrom - nLevTo ); // effective level + + f *= r.fConst / fConst; + + if( nLevFrom ) + f = ::rtl::math::pow10Exp( f, nLevFrom ); + + return f; +} + + +double ConvertData::ConvertFromBase( double f, sal_Int16 n ) const +{ + return ::rtl::math::pow10Exp( f * fConst, -n ); +} + +ConvertDataLinear::~ConvertDataLinear() +{ +} + +double ConvertDataLinear::Convert( + double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const +{ + if( Class() != r.Class() ) + throw lang::IllegalArgumentException(); + return r.ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo ); +} + + +double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const +{ + if( n ) + f = ::rtl::math::pow10Exp( f, n ); + + f /= fConst; + f -= fOffs; + + return f; +} + + +double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const +{ + f += fOffs; + f *= fConst; + + if( n ) + f = ::rtl::math::pow10Exp( f, -n ); + + return f; +} + + +ConvertDataList::ConvertDataList() +{ +#define NEWD(str,unit,cl) maVector.emplace_back(new ConvertData(str,unit,cl)) +#define NEWDP(str,unit,cl) maVector.emplace_back(new ConvertData(str,unit,cl,true)) +#define NEWL(str,unit,offs,cl) maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl)) +#define NEWLP(str,unit,offs,cl) maVector.emplace_back(new ConvertDataLinear(str,unit,offs,cl,true)) + + // *** are extra and not standard Excel Analysis Addin! + + // MASS: 1 Gram is... + NEWDP( "g", 1.0000000000000000E00, CDC_Mass ); // Gram + NEWD( "sg", 6.8522050005347800E-05, CDC_Mass ); // Pieces + NEWD( "lbm", 2.2046229146913400E-03, CDC_Mass ); // Pound (commercial weight) + NEWDP( "u", 6.0221370000000000E23, CDC_Mass ); // U (atomic mass) + NEWD( "ozm", 3.5273971800362700E-02, CDC_Mass ); // Ounce (commercial weight) + NEWD( "stone", 1.574730e-04, CDC_Mass ); // *** Stone + NEWD( "ton", 1.102311e-06, CDC_Mass ); // *** Ton + NEWD( "grain", 1.543236E01, CDC_Mass ); // *** Grain + NEWD( "pweight", 7.054792E-01, CDC_Mass ); // *** Pennyweight + NEWD( "hweight", 1.968413E-05, CDC_Mass ); // *** Hundredweight + NEWD( "shweight", 2.204623E-05, CDC_Mass ); // *** Shorthundredweight + NEWD( "brton", 9.842065E-07, CDC_Mass ); // *** Gross Registered Ton + NEWD( "cwt", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight + NEWD( "shweight", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight also + NEWD( "uk_cwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight + NEWD( "lcwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also + NEWD( "hweight", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also + NEWD( "uk_ton", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton + NEWD( "LTON", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton also + + // LENGTH: 1 Meter is... + NEWDP( "m", 1.0000000000000000E00, CDC_Length ); // Meter + NEWD( "mi", 6.2137119223733397E-04, CDC_Length ); // Britsh Mile 6,21371192237333969617434184363e-4 + NEWD( "Nmi", 5.3995680345572354E-04, CDC_Length ); // Nautical Mile 5,39956803455723542116630669546e-4 + NEWD( "in", 3.9370078740157480E01, CDC_Length ); // Inch 39,37007874015748031496062992126 + NEWD( "ft", 3.2808398950131234E00, CDC_Length ); // Foot 3,2808398950131233595800524934383 + NEWD( "yd", 1.0936132983377078E00, CDC_Length ); // Yard 1,0936132983377077865266841644794 + NEWDP( "ang", 1.0000000000000000E10, CDC_Length ); // Angstroem + NEWD( "Pica", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307 + NEWD( "picapt", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307 + NEWD( "pica", 2.36220472441E02, CDC_Length ); // pica (1/6 Inch) + NEWD( "ell", 8.748906E-01, CDC_Length ); // *** Ell + NEWDP( "parsec", 3.240779E-17, CDC_Length ); // *** Parsec + NEWDP( "pc", 3.240779E-17, CDC_Length ); // *** Parsec also + NEWDP( "lightyear", 1.0570234557732930E-16, CDC_Length ); // *** Light Year + NEWDP( "ly", 1.0570234557732930E-16, CDC_Length ); // *** Light Year also + NEWD( "survey_mi", 6.2136994949494949E-04, CDC_Length ); // U.S. survey mile + + // TIME: 1 Second is... + NEWD( "yr", 3.1688087814028950E-08, CDC_Time ); // Year + NEWD( "day", 1.1574074074074074E-05, CDC_Time ); // Day + NEWD( "d", 1.1574074074074074E-05, CDC_Time ); // Day also + NEWD( "hr", 2.7777777777777778E-04, CDC_Time ); // Hour + NEWD( "mn", 1.6666666666666667E-02, CDC_Time ); // Minute + NEWD( "min", 1.6666666666666667E-02, CDC_Time ); // Minute also + NEWDP( "sec", 1.0000000000000000E00, CDC_Time ); // Second + NEWDP( "s", 1.0000000000000000E00, CDC_Time ); // Second also + + // PRESSURE: 1 Pascal is... + NEWDP( "Pa", 1.0000000000000000E00, CDC_Pressure ); // Pascal + NEWDP( "atm", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere + NEWDP( "at", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere also + NEWDP( "mmHg", 7.5006170799862700E-03, CDC_Pressure ); // mm Hg (Mercury) + NEWD( "Torr", 7.5006380000000000E-03, CDC_Pressure ); // *** Torr + NEWD( "psi", 1.4503770000000000E-04, CDC_Pressure ); // *** Psi + + // FORCE: 1 Newton is... + NEWDP( "N", 1.0000000000000000E00, CDC_Force ); // Newton + NEWDP( "dyn", 1.0000000000000000E05, CDC_Force ); // Dyn + NEWDP( "dy", 1.0000000000000000E05, CDC_Force ); // Dyn also + NEWD( "lbf", 2.24808923655339E-01, CDC_Force ); // Pound-Force + NEWDP( "pond", 1.019716E02, CDC_Force ); // *** Pond + + // ENERGY: 1 Joule is... + NEWDP( "J", 1.0000000000000000E00, CDC_Energy ); // Joule + NEWDP( "e", 1.0000000000000000E07, CDC_Energy ); // Erg -> https://en.wikipedia.org/wiki/Erg + NEWDP( "c", 2.3900624947346700E-01, CDC_Energy ); // Thermodynamical Calorie + NEWDP( "cal", 2.3884619064201700E-01, CDC_Energy ); // Calorie + NEWDP( "eV", 6.2414570000000000E18, CDC_Energy ); // Electronvolt + NEWDP( "ev", 6.2414570000000000E18, CDC_Energy ); // Electronvolt also + NEWD( "HPh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours + NEWD( "hh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours also + NEWDP( "Wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours + NEWDP( "wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours also + NEWD( "flb", 2.37304222192651E01, CDC_Energy ); // Foot Pound + NEWD( "BTU", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit + NEWD( "btu", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit also + + // POWER: 1 Watt is... + NEWDP( "W", 1.0000000000000000E00, CDC_Power ); // Watt + NEWDP( "w", 1.0000000000000000E00, CDC_Power ); // Watt also + NEWD( "HP", 1.341022E-03, CDC_Power ); // Horsepower + NEWD( "h", 1.341022E-03, CDC_Power ); // Horsepower also + NEWD( "PS", 1.359622E-03, CDC_Power ); // *** German Pferdestaerke + + // MAGNETISM: 1 Tesla is... + NEWDP( "T", 1.0000000000000000E00, CDC_Magnetism ); // Tesla + NEWDP( "ga", 1.0000000000000000E04, CDC_Magnetism ); // Gauss + + // TEMPERATURE: 1 Kelvin is... + NEWL( "C", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius + NEWL( "cel", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius also + NEWL( "F", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit + NEWL( "fah", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit also + NEWLP( "K", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin + NEWLP( "kel", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin also + NEWL( "Reau", 8.0000000000000000E-01, -2.7315000000000000E02, CDC_Temperature ); // *** Reaumur + NEWL( "Rank", 1.8000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // *** Rankine + + // VOLUME: 1 Liter is... + NEWD( "tsp", 2.0288413621105798E02, CDC_Volume ); // US teaspoon 1/768 gallon + NEWD( "tbs", 6.7628045403685994E01, CDC_Volume ); // US tablespoon 1/256 gallon + NEWD( "oz", 3.3814022701842997E01, CDC_Volume ); // Ounce Liquid 1/128 gallon + NEWD( "cup", 4.2267528377303746E00, CDC_Volume ); // Cup 1/16 gallon + NEWD( "pt", 2.1133764188651873E00, CDC_Volume ); // US Pint 1/8 gallon + NEWD( "us_pt", 2.1133764188651873E00, CDC_Volume ); // US Pint also + NEWD( "uk_pt", 1.7597539863927023E00, CDC_Volume ); // UK Pint 1/8 imperial gallon + NEWD( "qt", 1.0566882094325937E00, CDC_Volume ); // Quart 1/4 gallon + NEWD( "gal", 2.6417205235814842E-01, CDC_Volume ); // Gallon 1/3.785411784 + NEWDP( "l", 1.0000000000000000E00, CDC_Volume ); // Liter + NEWDP( "L", 1.0000000000000000E00, CDC_Volume ); // Liter also + NEWDP( "lt", 1.0000000000000000E00, CDC_Volume ); // Liter also + NEWDP( "m3", 1.0000000000000000E-03, CDC_Volume ); // *** Cubic Meter + NEWD( "mi3", 2.3991275857892772E-13, CDC_Volume ); // *** Cubic Britsh Mile + NEWD( "Nmi3", 1.5742621468581148E-13, CDC_Volume ); // *** Cubic Nautical Mile + NEWD( "in3", 6.1023744094732284E01, CDC_Volume ); // *** Cubic Inch + NEWD( "ft3", 3.5314666721488590E-02, CDC_Volume ); // *** Cubic Foot + NEWD( "yd3", 1.3079506193143922E-03, CDC_Volume ); // *** Cubic Yard + NEWDP( "ang3", 1.0000000000000000E27, CDC_Volume ); // *** Cubic Angstroem + NEWD( "Pica3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch) + NEWD( "picapt3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch) + NEWD( "pica3", 1.31811287245E04, CDC_Volume ); // *** Cubic Pica (1/6 inch) + NEWD( "barrel", 6.2898107704321051E-03, CDC_Volume ); // *** Barrel (=42gal) + NEWD( "bushel", 2.837759E-02, CDC_Volume ); // *** Bushel + NEWD( "regton", 3.531467E-04, CDC_Volume ); // *** Register ton + NEWD( "GRT", 3.531467E-04, CDC_Volume ); // *** Register ton also + NEWD( "Schooner", 2.3529411764705882E00, CDC_Volume ); // *** austr. Schooner + NEWD( "Middy", 3.5087719298245614E00, CDC_Volume ); // *** austr. Middy + NEWD( "Glass", 5.0000000000000000E00, CDC_Volume ); // *** austr. Glass + NEWD( "Sixpack", 0.5, CDC_Volume ); // *** + NEWD( "Humpen", 2.0, CDC_Volume ); // *** + NEWD( "ly3", 1.1810108125623799E-51, CDC_Volume ); // *** Cubic light-year + NEWD( "MTON", 1.4125866688595436E00, CDC_Volume ); // *** Measurement ton + NEWD( "tspm", 2.0000000000000000E02, CDC_Volume ); // *** Modern teaspoon + NEWD( "uk_gal", 2.1996924829908779E-01, CDC_Volume ); // U.K. / Imperial gallon 1/4.54609 + NEWD( "uk_qt", 8.7987699319635115E-01, CDC_Volume ); // U.K. / Imperial quart 1/4 imperial gallon + + // 1 Square Meter is... + NEWDP( "m2", 1.0000000000000000E00, CDC_Area ); // *** Square Meter + NEWD( "mi2", 3.8610215854244585E-07, CDC_Area ); // *** Square Britsh Mile + NEWD( "Nmi2", 2.9155334959812286E-07, CDC_Area ); // *** Square Nautical Mile + NEWD( "in2", 1.5500031000062000E03, CDC_Area ); // *** Square Inch + NEWD( "ft2", 1.0763910416709722E01, CDC_Area ); // *** Square Foot + NEWD( "yd2", 1.1959900463010803E00, CDC_Area ); // *** Square Yard + NEWDP( "ang2", 1.0000000000000000E20, CDC_Area ); // *** Square Angstroem + NEWD( "Pica2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch) + NEWD( "picapt2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch) + NEWD( "pica2", 5.58001116002232E04, CDC_Area ); // *** Square Pica (1/6 inch) + NEWD( "Morgen", 4.0000000000000000E-04, CDC_Area ); // *** Morgen + NEWDP( "ar", 1.000000E-02, CDC_Area ); // *** Ar + NEWD( "acre", 2.471053815E-04, CDC_Area ); // *** Acre + NEWD( "uk_acre", 2.4710538146716534E-04, CDC_Area ); // *** International acre + NEWD( "us_acre", 2.4710439304662790E-04, CDC_Area ); // *** U.S. survey/statute acre + NEWD( "ly2", 1.1172985860549147E-32, CDC_Area ); // *** Square Light-year + NEWD( "ha", 1.000000E-04, CDC_Area ); // *** Hectare + + // SPEED: 1 Meter per Second is... + NEWDP( "m/s", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second + NEWDP( "m/sec", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second also + NEWDP( "m/h", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour + NEWDP( "m/hr", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour also + NEWD( "mph", 2.2369362920544023E00, CDC_Speed ); // *** Britsh Miles per Hour + NEWD( "kn", 1.9438444924406048E00, CDC_Speed ); // *** Knot = Nautical Miles per Hour + NEWD( "admkn", 1.9438446603753486E00, CDC_Speed ); // *** Admiralty Knot + NEWD( "ludicrous speed", 2.0494886343432328E-14, CDC_Speed ); // *** + NEWD( "ridiculous speed", 4.0156958471424288E-06, CDC_Speed); // *** + + // INFORMATION: 1 Bit is... + NEWDP( "bit", 1.00E00, CDC_Information); // *** Bit + NEWDP( "byte", 1.25E-01, CDC_Information); // *** Byte +} + + +ConvertDataList::~ConvertDataList() +{ +} + + +double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo ) +{ + ConvertData* pFrom = nullptr; + ConvertData* pTo = nullptr; + bool bSearchFrom = true; + bool bSearchTo = true; + sal_Int16 nLevelFrom = 0; + sal_Int16 nLevelTo = 0; + + for( const auto& rItem : maVector ) + { + ConvertData* p = rItem.get(); + if( bSearchFrom ) + { + sal_Int16 n = p->GetMatchingLevel( rFrom ); + if( n != INV_MATCHLEV ) + { + if( n ) + { // only first match for partial equality rulz a little bit more + pFrom = p; + nLevelFrom = n; + } + else + { // ... but exact match rulz most + pFrom = p; + bSearchFrom = false; + nLevelFrom = n; + } + } + } + + if( bSearchTo ) + { + sal_Int16 n = p->GetMatchingLevel( rTo ); + if( n != INV_MATCHLEV ) + { + if( n ) + { // only first match for partial equality rulz a little bit more + pTo = p; + nLevelTo = n; + } + else + { // ... but exact match rulz most + pTo = p; + bSearchTo = false; + nLevelTo = n; + } + } + } + + if( !bSearchFrom && !bSearchTo ) + break; + } + + if( !pFrom || !pTo ) + throw lang::IllegalArgumentException(); + + return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo ); +} + + +ScaDate::ScaDate() : + nOrigDay( 1 ), + nDay( 1 ), + nMonth( 1 ), + nYear( 1900 ), + bLastDayMode( true ), + bLastDay( false ), + b30Days( false ), + bUSMode( false ) +{ +} + +ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase ) +{ + DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear ); + bLastDayMode = (nBase != 5); + bLastDay = (nOrigDay >= ::DaysInMonth( nMonth, nYear )); + b30Days = (nBase == 0) || (nBase == 4); + bUSMode = (nBase == 0); + setDay(); +} + +ScaDate::ScaDate( const ScaDate& rCopy ) : + nOrigDay( rCopy.nOrigDay ), + nDay( rCopy.nDay ), + nMonth( rCopy.nMonth ), + nYear( rCopy.nYear ), + bLastDayMode( rCopy.bLastDayMode ), + bLastDay( rCopy.bLastDay ), + b30Days( rCopy.b30Days ), + bUSMode( rCopy.bUSMode ) +{ +} + +ScaDate& ScaDate::operator=( const ScaDate& rCopy ) +{ + if( this != &rCopy ) + { + nOrigDay = rCopy.nOrigDay; + nDay = rCopy.nDay; + nMonth = rCopy.nMonth; + nYear = rCopy.nYear; + bLastDayMode = rCopy.bLastDayMode; + bLastDay = rCopy.bLastDay; + b30Days = rCopy.b30Days; + bUSMode = rCopy.bUSMode; + } + return *this; +} + +void ScaDate::setDay() +{ + if( b30Days ) + { + // 30-days-mode: set nDay to 30 if original was last day in month + nDay = std::min( nOrigDay, static_cast< sal_uInt16 >( 30 ) ); + if( bLastDay || (nDay >= ::DaysInMonth( nMonth, nYear )) ) + nDay = 30; + } + else + { + // set nDay to last day in this month if original was last day + sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear ); + nDay = bLastDay ? nLastDay : std::min( nOrigDay, nLastDay ); + } +} + +sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const +{ + if( nFrom > nTo ) + return 0; + + sal_Int32 nRet = 0; + if( b30Days ) + nRet = (nTo - nFrom + 1) * 30; + else + { + for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx ) + nRet += getDaysInMonth( nMonthIx ); + } + return nRet; +} + +sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const +{ + if( nFrom > nTo ) + return 0; + + return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo ); +} + +void ScaDate::doAddYears( sal_Int32 nYearCount ) +{ + sal_Int32 nNewYear = nYearCount + nYear; + if( (nNewYear < 0) || (nNewYear > 0x7FFF) ) + throw lang::IllegalArgumentException(); + nYear = static_cast< sal_uInt16 >( nNewYear ); +} + +void ScaDate::addMonths( sal_Int32 nMonthCount ) +{ + sal_Int32 nNewMonth = nMonthCount + nMonth; + if( nNewMonth > 12 ) + { + --nNewMonth; + doAddYears( nNewMonth / 12 ); + nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1; + } + else if( nNewMonth < 1 ) + { + doAddYears( nNewMonth / 12 - 1 ); + nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 ); + } + else + nMonth = static_cast< sal_uInt16 >( nNewMonth ); + setDay(); +} + +sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const +{ + sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear ); + sal_uInt16 nRealDay = (bLastDayMode && bLastDay) ? nLastDay : std::min( nLastDay, nOrigDay ); + return ::DateToDays( nRealDay, nMonth, nYear ) - nNullDate; +} + +sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo ) +{ + if( rFrom > rTo ) + return getDiff( rTo, rFrom ); + + sal_Int32 nDiff = 0; + ScaDate aFrom( rFrom ); + ScaDate aTo( rTo ); + + if( rTo.b30Days ) + { + // corrections for base 0 (US NASD) + if( rTo.bUSMode ) + { + if( ((rFrom.nMonth == 2) || (rFrom.nDay < 30)) && (aTo.nOrigDay == 31) ) + aTo.nDay = 31; + else if( (aTo.nMonth == 2) && aTo.bLastDay ) + aTo.nDay = ::DaysInMonth( 2, aTo.nYear ); + } + // corrections for base 4 (Europe) + else + { + if( (aFrom.nMonth == 2) && (aFrom.nDay == 30) ) + aFrom.nDay = ::DaysInMonth( 2, aFrom.nYear ); + if( (aTo.nMonth == 2) && (aTo.nDay == 30) ) + aTo.nDay = ::DaysInMonth( 2, aTo.nYear ); + } + } + + if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) ) + { + // move aFrom to 1st day of next month + nDiff = aFrom.getDaysInMonth() - aFrom.nDay + 1; + aFrom.nOrigDay = aFrom.nDay = 1; + aFrom.bLastDay = false; + aFrom.addMonths( 1 ); + + if( aFrom.nYear < aTo.nYear ) + { + // move aFrom to 1st day of next year + nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 ); + aFrom.addMonths( 13 - aFrom.nMonth ); + + // move aFrom to 1st day of this year + nDiff += aFrom.getDaysInYearRange( aFrom.nYear, aTo.nYear - 1 ); + aFrom.addYears( aTo.nYear - aFrom.nYear ); + } + + // move aFrom to 1st day of this month + nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, aTo.nMonth - 1 ); + aFrom.addMonths( aTo.nMonth - aFrom.nMonth ); + } + // finally add remaining days in this month + nDiff += aTo.nDay - aFrom.nDay; + return std::max<sal_Int32>(nDiff, 0); +} + +bool ScaDate::operator<( const ScaDate& rCmp ) const +{ + if( nYear != rCmp.nYear ) + return nYear < rCmp.nYear; + if( nMonth != rCmp.nMonth ) + return nMonth < rCmp.nMonth; + if( nDay != rCmp.nDay ) + return nDay < rCmp.nDay; + if( bLastDay || rCmp.bLastDay ) + return !bLastDay && rCmp.bLastDay; + return nOrigDay < rCmp.nOrigDay; +} + + +ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext ) + : nDefaultFormat(0) + , bHasValidFormat(false) +{ + xFormatter = util::NumberFormatter::create(xContext); +} + +ScaAnyConverter::~ScaAnyConverter() +{ +} + +void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet ) +{ + // try to get default number format + bHasValidFormat = false; + if( !xFormatter.is() ) + return; + + // get XFormatsSupplier from outer XPropertySet + uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY ); + if( !xFormatsSupp.is() ) + return; + + // get XNumberFormatTypes from XNumberFormatsSupplier to get standard index + uno::Reference< util::XNumberFormats > xFormats( xFormatsSupp->getNumberFormats() ); + uno::Reference< util::XNumberFormatTypes > xFormatTypes( xFormats, uno::UNO_QUERY ); + if( xFormatTypes.is() ) + { + lang::Locale eLocale; + nDefaultFormat = xFormatTypes->getStandardIndex( eLocale ); + xFormatter->attachNumberFormatsSupplier( xFormatsSupp ); + bHasValidFormat = true; + } +} + +double ScaAnyConverter::convertToDouble( const OUString& rString ) const +{ + double fValue = 0.0; + if( bHasValidFormat ) + { + try + { + fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString ); + } + catch( uno::Exception& ) + { + throw lang::IllegalArgumentException(); + } + } + else + { + rtl_math_ConversionStatus eStatus; + sal_Int32 nEnd; + fValue = ::rtl::math::stringToDouble( rString, '.', ',', &eStatus, &nEnd ); + if( (eStatus != rtl_math_ConversionStatus_Ok) || (nEnd < rString.getLength()) ) + throw lang::IllegalArgumentException(); + } + return fValue; +} + +bool ScaAnyConverter::getDouble( + double& rfResult, + const uno::Any& rAny ) const +{ + rfResult = 0.0; + bool bContainsVal = true; + switch( rAny.getValueTypeClass() ) + { + case uno::TypeClass_VOID: + bContainsVal = false; + break; + case uno::TypeClass_DOUBLE: + rAny >>= rfResult; + break; + case uno::TypeClass_STRING: + { + auto pString = o3tl::forceAccess< OUString >( rAny ); + if( !pString->isEmpty() ) + rfResult = convertToDouble( *pString ); + else + bContainsVal = false; + } + break; + default: + throw lang::IllegalArgumentException(); + } + return bContainsVal; +} + +bool ScaAnyConverter::getDouble( + double& rfResult, + const uno::Reference< beans::XPropertySet >& xPropSet, + const uno::Any& rAny ) +{ + init( xPropSet ); + return getDouble( rfResult, rAny ); +} + +double ScaAnyConverter::getDouble( + const uno::Reference< beans::XPropertySet >& xPropSet, + const uno::Any& rAny, + double fDefault ) +{ + double fResult; + if( !getDouble( fResult, xPropSet, rAny ) ) + fResult = fDefault; + return fResult; +} + +bool ScaAnyConverter::getInt32( + sal_Int32& rnResult, + const uno::Reference< beans::XPropertySet >& xPropSet, + const uno::Any& rAny ) +{ + double fResult; + bool bContainsVal = getDouble( fResult, xPropSet, rAny ); + if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) ) + throw lang::IllegalArgumentException(); + + rnResult = static_cast< sal_Int32 >( fResult ); + return bContainsVal; +} + +sal_Int32 ScaAnyConverter::getInt32( + const uno::Reference< beans::XPropertySet >& xPropSet, + const uno::Any& rAny, + sal_Int32 nDefault ) +{ + sal_Int32 nResult; + if( !getInt32( nResult, xPropSet, rAny ) ) + nResult = nDefault; + return nResult; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |