diff options
Diffstat (limited to 'scaddins/source')
-rw-r--r-- | scaddins/source/analysis/analysis.component | 26 | ||||
-rw-r--r-- | scaddins/source/analysis/analysis.cxx | 1093 | ||||
-rw-r--r-- | scaddins/source/analysis/analysis.hxx | 240 | ||||
-rw-r--r-- | scaddins/source/analysis/analysisdefs.hxx | 29 | ||||
-rw-r--r-- | scaddins/source/analysis/analysishelper.cxx | 2813 | ||||
-rw-r--r-- | scaddins/source/analysis/analysishelper.hxx | 911 | ||||
-rw-r--r-- | scaddins/source/analysis/bessel.cxx | 456 | ||||
-rw-r--r-- | scaddins/source/analysis/bessel.hxx | 65 | ||||
-rw-r--r-- | scaddins/source/analysis/deffuncname.hxx | 631 | ||||
-rw-r--r-- | scaddins/source/analysis/financial.cxx | 676 | ||||
-rw-r--r-- | scaddins/source/datefunc/date.component | 26 | ||||
-rw-r--r-- | scaddins/source/datefunc/datefunc.cxx | 760 | ||||
-rw-r--r-- | scaddins/source/datefunc/datefunc.hxx | 198 | ||||
-rw-r--r-- | scaddins/source/datefunc/deffuncname.hxx | 73 | ||||
-rw-r--r-- | scaddins/source/pricing/black_scholes.cxx | 940 | ||||
-rw-r--r-- | scaddins/source/pricing/black_scholes.hxx | 149 | ||||
-rw-r--r-- | scaddins/source/pricing/pricing.component | 26 | ||||
-rw-r--r-- | scaddins/source/pricing/pricing.cxx | 545 | ||||
-rw-r--r-- | scaddins/source/pricing/pricing.hxx | 209 |
19 files changed, 9866 insertions, 0 deletions
diff --git a/scaddins/source/analysis/analysis.component b/scaddins/source/analysis/analysis.component new file mode 100644 index 000000000..d20aae77d --- /dev/null +++ b/scaddins/source/analysis/analysis.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="analysis" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sheet.addin.AnalysisImpl"> + <service name="com.sun.star.sheet.AddIn"/> + <service name="com.sun.star.sheet.addin.Analysis"/> + </implementation> +</component> diff --git a/scaddins/source/analysis/analysis.cxx b/scaddins/source/analysis/analysis.cxx new file mode 100644 index 000000000..2669a00c2 --- /dev/null +++ b/scaddins/source/analysis/analysis.cxx @@ -0,0 +1,1093 @@ +/* -*- 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 "analysisdefs.hxx" +#include "analysis.hxx" +#include "bessel.hxx" +#include <cppuhelper/factory.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/random.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <o3tl/any.hxx> +#include <rtl/math.hxx> +#include <sal/macros.h> +#include <unotools/resmgr.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <algorithm> +#include <cmath> +#include <float.h> + +#define ADDIN_SERVICE "com.sun.star.sheet.AddIn" +#define MY_SERVICE "com.sun.star.sheet.addin.Analysis" +#define MY_IMPLNAME "com.sun.star.sheet.addin.AnalysisImpl" + +using namespace ::com::sun::star; +using namespace sca::analysis; +using namespace std; + +extern "C" SAL_DLLPUBLIC_EXPORT void* analysis_component_getFactory( + const char* pImplName, void* pServiceManager, void* /*pRegistryKey*/ ) +{ + void* pRet = nullptr; + + if( pServiceManager && OUString::createFromAscii( pImplName ) == AnalysisAddIn::getImplementationName_Static() ) + { + uno::Reference< lang::XSingleServiceFactory > xFactory( cppu::createOneInstanceFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + AnalysisAddIn::getImplementationName_Static(), + AnalysisAddIn_CreateInstance, + AnalysisAddIn::getSupportedServiceNames_Static() ) ); + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +OUString AnalysisAddIn::GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex) +{ + return AnalysisResId(pResId[nStrIndex - 1]); +} + +void AnalysisAddIn::InitData() +{ + aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc)); + + pFD.reset(new FuncDataList); + InitFuncDataList(*pFD); + + pDefLocales.reset(); +} + +AnalysisAddIn::AnalysisAddIn( const uno::Reference< uno::XComponentContext >& xContext ) : + aAnyConv( xContext ) +{ +} + +AnalysisAddIn::~AnalysisAddIn() +{ +} + +sal_Int32 AnalysisAddIn::getDateMode( + const uno::Reference< beans::XPropertySet >& xPropSet, + const uno::Any& rAny ) +{ + sal_Int32 nMode = aAnyConv.getInt32( xPropSet, rAny, 0 ); + if( (nMode < 0) || (nMode > 4) ) + throw lang::IllegalArgumentException(); + return nMode; +} + +#define MAXFACTDOUBLE 300 + +double AnalysisAddIn::FactDouble( sal_Int32 nNum ) +{ + if( nNum < 0 || nNum > MAXFACTDOUBLE ) + throw lang::IllegalArgumentException(); + + if( !pFactDoubles ) + { + pFactDoubles.reset( new double[ MAXFACTDOUBLE + 1 ] ); + + pFactDoubles[ 0 ] = 1.0; // by default + + double fOdd = 1.0; + double fEven = 2.0; + + pFactDoubles[ 1 ] = fOdd; + pFactDoubles[ 2 ] = fEven; + + bool bOdd = true; + + for( sal_uInt16 nCnt = 3 ; nCnt <= MAXFACTDOUBLE ; nCnt++ ) + { + if( bOdd ) + { + fOdd *= nCnt; + pFactDoubles[ nCnt ] = fOdd; + } + else + { + fEven *= nCnt; + pFactDoubles[ nCnt ] = fEven; + } + + bOdd = !bOdd; + + } + } + + return pFactDoubles[ nNum ]; +} + +OUString AnalysisAddIn::getImplementationName_Static() +{ + return MY_IMPLNAME; +} + +uno::Sequence< OUString > AnalysisAddIn::getSupportedServiceNames_Static() +{ + return { ADDIN_SERVICE, MY_SERVICE }; +} + +uno::Reference< uno::XInterface > AnalysisAddIn_CreateInstance( + const uno::Reference< lang::XMultiServiceFactory >& xServiceFact ) +{ + return static_cast<cppu::OWeakObject*>(new AnalysisAddIn( comphelper::getComponentContext(xServiceFact) )); +} + +// XServiceName +OUString SAL_CALL AnalysisAddIn::getServiceName() +{ + // name of specific AddIn service + return MY_SERVICE; +} + +// XServiceInfo +OUString SAL_CALL AnalysisAddIn::getImplementationName() +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL AnalysisAddIn::supportsService( const OUString& aName ) +{ + return cppu::supportsService(this, aName); +} + +uno::Sequence< OUString > SAL_CALL AnalysisAddIn::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +// XLocalizable +void SAL_CALL AnalysisAddIn::setLocale( const lang::Locale& eLocale ) +{ + aFuncLoc = eLocale; + + InitData(); // change of locale invalidates resources! +} + +lang::Locale SAL_CALL AnalysisAddIn::getLocale() +{ + return aFuncLoc; +} + +// XAddIn +OUString SAL_CALL AnalysisAddIn::getProgrammaticFuntionName( const OUString& ) +{ + // not used by calc + // (but should be implemented for other uses of the AddIn service) + + return OUString(); +} + +OUString SAL_CALL AnalysisAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) ); + if( it != pFD->end() ) + { + aRet = AnalysisResId(it->GetUINameID()); + if( it->IsDouble() ) + { + const OUString& rSuffix = it->GetSuffix(); + if (!rSuffix.isEmpty()) + aRet += rSuffix; + else + aRet += "_ADD"; + } + } + else + { + aRet = "UNKNOWNFUNC_" + aProgrammaticName; + } + + return aRet; +} + +OUString SAL_CALL AnalysisAddIn::getFunctionDescription( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) ); + if( it != pFD->end() ) + aRet = GetFuncDescrStr( it->GetDescrID(), 1 ); + + return aRet; +} + +OUString SAL_CALL AnalysisAddIn::getDisplayArgumentName( const OUString& aName, sal_Int32 nArg ) +{ + OUString aRet; + + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) ); + if( it != pFD->end() && nArg <= 0xFFFF ) + { + sal_uInt16 nStr = it->GetStrIndex( sal_uInt16( nArg ) ); + if( nStr ) + aRet = GetFuncDescrStr( it->GetDescrID(), nStr ); + else + aRet = "internal"; + } + + return aRet; +} + +OUString SAL_CALL AnalysisAddIn::getArgumentDescription( const OUString& aName, sal_Int32 nArg ) +{ + OUString aRet; + + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) ); + if( it != pFD->end() && nArg <= 0xFFFF ) + { + sal_uInt16 nStr = it->GetStrIndex( sal_uInt16( nArg ) ); + if( nStr ) + aRet = GetFuncDescrStr( it->GetDescrID(), nStr + 1 ); + else + aRet = "for internal use only"; + } + + return aRet; +} + +static const char pDefCatName[] = "Add-In"; + +OUString SAL_CALL AnalysisAddIn::getProgrammaticCategoryName( const OUString& aName ) +{ + // return non-translated strings + // return OUString( "Add-In" ); + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) ); + OUString aRet; + if( it != pFD->end() ) + { + switch( it->GetCategory() ) + { + case FDCategory::DateTime: aRet = "Date&Time"; break; + case FDCategory::Finance: aRet = "Financial"; break; + case FDCategory::Inf: aRet = "Information"; break; + case FDCategory::Math: aRet = "Mathematical"; break; + case FDCategory::Tech: aRet = "Technical"; break; + } + } + else + aRet = pDefCatName; + + return aRet; +} + +OUString SAL_CALL AnalysisAddIn::getDisplayCategoryName( const OUString& aProgrammaticFunctionName ) +{ + // return translated strings, not used for predefined categories + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticFunctionName ) ); + OUString aRet; + if( it != pFD->end() ) + { + switch( it->GetCategory() ) + { + case FDCategory::DateTime: aRet = "Date&Time"; break; + case FDCategory::Finance: aRet = "Financial"; break; + case FDCategory::Inf: aRet = "Information"; break; + case FDCategory::Math: aRet = "Mathematical"; break; + case FDCategory::Tech: aRet = "Technical"; break; + } + } + else + aRet = pDefCatName; + + return aRet; +} + +static const char* pLang[] = { "de", "en" }; +static const char* pCoun[] = { "DE", "US" }; +static const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS(pLang); + +void AnalysisAddIn::InitDefLocales() +{ + pDefLocales.reset( new lang::Locale[ nNumOfLoc ] ); + + for( sal_uInt32 n = 0 ; n < nNumOfLoc ; n++ ) + { + pDefLocales[ n ].Language = OUString::createFromAscii( pLang[ n ] ); + pDefLocales[ n ].Country = OUString::createFromAscii( pCoun[ n ] ); + } +} + +inline const lang::Locale& AnalysisAddIn::GetLocale( sal_uInt32 nInd ) +{ + if( !pDefLocales ) + InitDefLocales(); + + if( nInd < sizeof( pLang ) ) + return pDefLocales[ nInd ]; + else + return aFuncLoc; +} + +uno::Sequence< sheet::LocalizedName > SAL_CALL AnalysisAddIn::getCompatibilityNames( const OUString& aProgrammaticName ) +{ + auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) ); + if( it == pFD->end() ) + return uno::Sequence< sheet::LocalizedName >( 0 ); + + const std::vector<OUString>& r = it->GetCompNameList(); + sal_uInt32 nCount = r.size(); + + uno::Sequence< sheet::LocalizedName > aRet( nCount ); + + sheet::LocalizedName* pArray = aRet.getArray(); + + for( sal_uInt32 n = 0 ; n < nCount ; n++ ) + { + pArray[ n ] = sheet::LocalizedName( GetLocale( n ), r[n] ); + } + + return aRet; +} + +// XAnalysis +/** Workday */ +sal_Int32 SAL_CALL AnalysisAddIn::getWorkday( const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nDate, sal_Int32 nDays, const uno::Any& aHDay ) +{ + if( !nDays ) + return nDate; + + sal_Int32 nNullDate = GetNullDate( xOptions ); + + SortedIndividualInt32List aSrtLst; + + aSrtLst.InsertHolidayList( aAnyConv, xOptions, aHDay, nNullDate ); + + sal_Int32 nActDate = nDate + nNullDate; + + if( nDays > 0 ) + { + if( GetDayOfWeek( nActDate ) == 5 ) + // when starting on Saturday, assuming we're starting on Sunday to get the jump over the weekend + nActDate++; + + while( nDays ) + { + nActDate++; + + if( GetDayOfWeek( nActDate ) < 5 ) + { + if( !aSrtLst.Find( nActDate ) ) + nDays--; + } + else + nActDate++; // jump over weekend + } + } + else + { + if( GetDayOfWeek( nActDate ) == 6 ) + // when starting on Sunday, assuming we're starting on Saturday to get the jump over the weekend + nActDate--; + + while( nDays ) + { + nActDate--; + + if( GetDayOfWeek( nActDate ) < 5 ) + { + if( !aSrtLst.Find( nActDate ) ) + nDays++; + } + else + nActDate--; // jump over weekend + } + } + + return nActDate - nNullDate; +} + +/** Yearfrac */ +double SAL_CALL AnalysisAddIn::getYearfrac( const uno::Reference< beans::XPropertySet >& xOpt, + sal_Int32 nStartDate, sal_Int32 nEndDate, const uno::Any& rMode ) +{ + double fRet = GetYearFrac( xOpt, nStartDate, nEndDate, getDateMode( xOpt, rMode ) ); + RETURN_FINITE( fRet ); +} + +sal_Int32 SAL_CALL AnalysisAddIn::getEdate( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nStartDate, sal_Int32 nMonths ) +{ + sal_Int32 nNullDate = GetNullDate( xOpt ); + ScaDate aDate( nNullDate, nStartDate, 5 ); + aDate.addMonths( nMonths ); + return aDate.getDate( nNullDate ); +} + +sal_Int32 SAL_CALL AnalysisAddIn::getWeeknum( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nDate, sal_Int32 nMode ) +{ + nDate += GetNullDate( xOpt ); + + sal_uInt16 nDay, nMonth, nYear; + DaysToDate( nDate, nDay, nMonth, nYear ); + + sal_Int32 nFirstInYear = DateToDays( 1, 1, nYear ); + sal_uInt16 nFirstDayInYear = GetDayOfWeek( nFirstInYear ); + + return ( nDate - nFirstInYear + ( ( nMode == 1 )? ( nFirstDayInYear + 1 ) % 7 : nFirstDayInYear ) ) / 7 + 1; +} + +sal_Int32 SAL_CALL AnalysisAddIn::getEomonth( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nDate, sal_Int32 nMonths ) +{ + sal_Int32 nNullDate = GetNullDate( xOpt ); + nDate += nNullDate; + sal_uInt16 nDay, nMonth, nYear; + DaysToDate( nDate, nDay, nMonth, nYear ); + + sal_Int32 nNewMonth = nMonth + nMonths; + + if( nNewMonth > 12 ) + { + nYear = sal::static_int_cast<sal_uInt16>( nYear + ( nNewMonth / 12 ) ); + nNewMonth %= 12; + } + else if( nNewMonth < 1 ) + { + nNewMonth = -nNewMonth; + nYear = sal::static_int_cast<sal_uInt16>( nYear - ( nNewMonth / 12 ) ); + nYear--; + nNewMonth %= 12; + nNewMonth = 12 - nNewMonth; + } + + return DateToDays( DaysInMonth( sal_uInt16( nNewMonth ), nYear ), sal_uInt16( nNewMonth ), nYear ) - nNullDate; +} + +sal_Int32 SAL_CALL AnalysisAddIn::getNetworkdays( const uno::Reference< beans::XPropertySet >& xOpt, + sal_Int32 nStartDate, sal_Int32 nEndDate, const uno::Any& aHDay ) +{ + sal_Int32 nNullDate = GetNullDate( xOpt ); + + SortedIndividualInt32List aSrtLst; + + aSrtLst.InsertHolidayList( aAnyConv, xOpt, aHDay, nNullDate ); + + sal_Int32 nActDate = nStartDate + nNullDate; + sal_Int32 nStopDate = nEndDate + nNullDate; + sal_Int32 nCnt = 0; + + if( nActDate <= nStopDate ) + { + while( nActDate <= nStopDate ) + { + if( GetDayOfWeek( nActDate ) < 5 && !aSrtLst.Find( nActDate ) ) + nCnt++; + + nActDate++; + } + } + else + { + while( nActDate >= nStopDate ) + { + if( GetDayOfWeek( nActDate ) < 5 && !aSrtLst.Find( nActDate ) ) + nCnt--; + + nActDate--; + } + } + + return nCnt; +} + +sal_Int32 SAL_CALL AnalysisAddIn::getIseven( sal_Int32 nVal ) +{ + return ( nVal & 0x00000001 )? 0 : 1; +} + +sal_Int32 SAL_CALL AnalysisAddIn::getIsodd( sal_Int32 nVal ) +{ + return ( nVal & 0x00000001 )? 1 : 0; +} + +double SAL_CALL +AnalysisAddIn::getMultinomial( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< sal_Int32 > >& aVLst, + const uno::Sequence< uno::Any >& aOptVLst ) +{ + ScaDoubleListGE0 aValList; + + aValList.Append( aVLst ); + aValList.Append( aAnyConv, xOpt, aOptVLst ); + + if( aValList.Count() == 0 ) + return 0.0; + + double nZ = 0; + double fRet = 1.0; + + for( sal_uInt32 i = 0; i < aValList.Count(); ++i ) + { + const double d = aValList.Get(i); + double n = (d >= 0.0) ? rtl::math::approxFloor( d ) : rtl::math::approxCeil( d ); + if ( n < 0.0 ) + throw lang::IllegalArgumentException(); + + if( n > 0.0 ) + { + nZ += n; + fRet *= BinomialCoefficient(nZ, n); + } + } + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getSeriessum( double fX, double fN, double fM, const uno::Sequence< uno::Sequence< double > >& aCoeffList ) +{ + double fRet = 0.0; + + // #i32269# 0^0 is undefined, Excel returns #NUM! error + if( fX == 0.0 && fN == 0 ) + throw uno::RuntimeException("undefined expression: 0^0"); + + if( fX != 0.0 ) + { + for( const uno::Sequence< double >& rList : aCoeffList ) + { + for( const double fCoef : rList ) + { + fRet += fCoef * pow( fX, fN ); + + fN += fM; + } + } + } + + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getQuotient( double fNum, double fDenom ) +{ + double fRet; + if( (fNum < 0) != (fDenom < 0) ) + fRet = ::rtl::math::approxCeil( fNum / fDenom ); + else + fRet = ::rtl::math::approxFloor( fNum / fDenom ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getMround( double fNum, double fMult ) +{ + if( fMult == 0.0 ) + return fMult; + + double fRet = fMult * ::rtl::math::round( fNum / fMult ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getSqrtpi( double fNum ) +{ + double fRet = sqrt( fNum * PI ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getRandbetween( double fMin, double fMax ) +{ + fMin = ::rtl::math::round( fMin, 0, rtl_math_RoundingMode_Up ); + fMax = ::rtl::math::round( fMax, 0, rtl_math_RoundingMode_Up ); + if( fMin > fMax ) + throw lang::IllegalArgumentException(); + + double fRet = floor(comphelper::rng::uniform_real_distribution(fMin, nextafter(fMax+1, -DBL_MAX))); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getGcd( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< double > >& aVLst, const uno::Sequence< uno::Any >& aOptVLst ) +{ + ScaDoubleListGT0 aValList; + + aValList.Append( aVLst ); + aValList.Append( aAnyConv, xOpt, aOptVLst ); + + if( aValList.Count() == 0 ) + return 0.0; + + double f = aValList.Get(0); + for( sal_uInt32 i = 1; i < aValList.Count(); ++i ) + { + f = GetGcd( aValList.Get(i), f ); + } + + RETURN_FINITE( f ); +} + +double SAL_CALL AnalysisAddIn::getLcm( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< double > >& aVLst, const uno::Sequence< uno::Any >& aOptVLst ) +{ + ScaDoubleListGE0 aValList; + + aValList.Append( aVLst ); + aValList.Append( aAnyConv, xOpt, aOptVLst ); + + if( aValList.Count() == 0 ) + return 0.0; + + double f = rtl::math::approxFloor( aValList.Get(0) ); + if( f < 0.0 ) + throw lang::IllegalArgumentException(); + + if( f == 0.0 ) + return f; + + for( sal_uInt32 i = 1; i < aValList.Count(); ++i ) + { + double fTmp = rtl::math::approxFloor( aValList.Get(i) ); + if( fTmp < 0.0 ) + throw lang::IllegalArgumentException(); + + f = fTmp * f / GetGcd( fTmp, f ); + if( f == 0.0 ) + return f; + } + + RETURN_FINITE( f ); +} + +double SAL_CALL AnalysisAddIn::getBesseli( double fNum, sal_Int32 nOrder ) +{ + double fRet = sca::analysis::BesselI( fNum, nOrder ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getBesselj( double fNum, sal_Int32 nOrder ) +{ + double fRet = sca::analysis::BesselJ( fNum, nOrder ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getBesselk( double fNum, sal_Int32 nOrder ) +{ + if( nOrder < 0 || fNum <= 0.0 ) + throw lang::IllegalArgumentException(); + + double fRet = sca::analysis::BesselK( fNum, nOrder ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getBessely( double fNum, sal_Int32 nOrder ) +{ + if( nOrder < 0 || fNum <= 0.0 ) + throw lang::IllegalArgumentException(); + + double fRet = sca::analysis::BesselY( fNum, nOrder ); + RETURN_FINITE( fRet ); +} + +const double SCA_MAX2 = 511.0; // min. val for binary numbers (9 bits + sign) +const double SCA_MIN2 = -SCA_MAX2-1.0; // min. val for binary numbers (9 bits + sign) +const double SCA_MAX8 = 536870911.0; // max. val for octal numbers (29 bits + sign) +const double SCA_MIN8 = -SCA_MAX8-1.0; // min. val for octal numbers (29 bits + sign) +const double SCA_MAX16 = 549755813888.0; // max. val for hexadecimal numbers (39 bits + sign) +const double SCA_MIN16 = -SCA_MAX16-1.0; // min. val for hexadecimal numbers (39 bits + sign) +const sal_Int32 SCA_MAXPLACES = 10; // max. number of places + +OUString SAL_CALL AnalysisAddIn::getBin2Oct( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 2, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +double SAL_CALL AnalysisAddIn::getBin2Dec( const OUString& aNum ) +{ + double fRet = ConvertToDec( aNum, 2, SCA_MAXPLACES ); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getBin2Hex( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 2, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +OUString SAL_CALL AnalysisAddIn::getOct2Bin( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 8, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +double SAL_CALL AnalysisAddIn::getOct2Dec( const OUString& aNum ) +{ + double fRet = ConvertToDec( aNum, 8, SCA_MAXPLACES ); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getOct2Hex( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 8, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +OUString SAL_CALL AnalysisAddIn::getDec2Bin( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nNum, const uno::Any& rPlaces ) +{ + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( nNum, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +OUString SAL_CALL AnalysisAddIn::getDec2Oct( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nNum, const uno::Any& rPlaces ) +{ + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( nNum, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +OUString SAL_CALL AnalysisAddIn::getDec2Hex( const uno::Reference< beans::XPropertySet >& xOpt, double fNum, const uno::Any& rPlaces ) +{ + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fNum, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +OUString SAL_CALL AnalysisAddIn::getHex2Bin( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 16, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +double SAL_CALL AnalysisAddIn::getHex2Dec( const OUString& aNum ) +{ + double fRet = ConvertToDec( aNum, 16, SCA_MAXPLACES ); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getHex2Oct( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces ) +{ + double fVal = ConvertToDec( aNum, 16, SCA_MAXPLACES ); + sal_Int32 nPlaces = 0; + bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces ); + return ConvertFromDec( fVal, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces ); +} + +sal_Int32 SAL_CALL AnalysisAddIn::getDelta( const uno::Reference< beans::XPropertySet >& xOpt, double fNum1, const uno::Any& rNum2 ) +{ + return sal_Int32(fNum1 == aAnyConv.getDouble( xOpt, rNum2, 0.0 )); +} + +double SAL_CALL AnalysisAddIn::getErf( const uno::Reference< beans::XPropertySet >& xOpt, double fLL, const uno::Any& rUL ) +{ + double fUL, fRet; + bool bContainsValue = aAnyConv.getDouble( fUL, xOpt, rUL ); + + fRet = bContainsValue ? (Erf( fUL ) - Erf( fLL )) : Erf( fLL ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getErfc( double f ) +{ + double fRet = Erfc( f ); + RETURN_FINITE( fRet ); +} + +sal_Int32 SAL_CALL AnalysisAddIn::getGestep( const uno::Reference< beans::XPropertySet >& xOpt, double fNum, const uno::Any& rStep ) +{ + return sal_Int32(fNum >= aAnyConv.getDouble( xOpt, rStep, 0.0 )); +} + +double SAL_CALL AnalysisAddIn::getFactdouble( sal_Int32 nNum ) +{ + double fRet = FactDouble( nNum ); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getImabs( const OUString& aNum ) +{ + double fRet = Complex( aNum ).Abs(); + RETURN_FINITE( fRet ); +} + +double SAL_CALL AnalysisAddIn::getImaginary( const OUString& aNum ) +{ + double fRet = Complex( aNum ).Imag(); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getImpower( const OUString& aNum, double f ) +{ + Complex z( aNum ); + + z.Power( f ); + + return z.GetString(); +} + +double SAL_CALL AnalysisAddIn::getImargument( const OUString& aNum ) +{ + double fRet = Complex( aNum ).Arg(); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getImcos( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Cos(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImdiv( const OUString& aDivid, const OUString& aDivis ) +{ + Complex z( aDivid ); + + z.Div( Complex( aDivis ) ); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImexp( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Exp(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImconjugate( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Conjugate(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImln( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Ln(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImlog10( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Log10(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImlog2( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Log2(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImproduct( const uno::Reference< beans::XPropertySet >&, const uno::Sequence< uno::Sequence< OUString > >& aNum1, const uno::Sequence< uno::Any >& aNL ) +{ + ComplexList z_list; + + z_list.Append( aNum1 ); + z_list.Append( aNL ); + + if( z_list.empty() ) + return Complex( 0 ).GetString(); + + Complex z = z_list.Get(0); + for( sal_uInt32 i = 1; i < z_list.Count(); ++i ) + z.Mult( z_list.Get(i) ); + + return z.GetString(); +} + +double SAL_CALL AnalysisAddIn::getImreal( const OUString& aNum ) +{ + double fRet = Complex( aNum ).Real(); + RETURN_FINITE( fRet ); +} + +OUString SAL_CALL AnalysisAddIn::getImsin( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Sin(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsub( const OUString& aNum1, const OUString& aNum2 ) +{ + Complex z( aNum1 ); + + z.Sub( Complex( aNum2 ) ); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsum( const uno::Reference< beans::XPropertySet >&, const uno::Sequence< uno::Sequence< OUString > >& aNum1, const uno::Sequence< uno::Any >& aFollowingPars ) +{ + ComplexList z_list; + + z_list.Append( aNum1 ); + z_list.Append( aFollowingPars ); + + if( z_list.empty() ) + return Complex( 0 ).GetString(); + + Complex z( z_list.Get(0) ); + for( sal_uInt32 i = 1; i < z_list.Count(); ++i ) + z.Add( z_list.Get(i) ); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsqrt( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Sqrt(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImtan( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Tan(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsec( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Sec(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImcsc( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Csc(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImcot( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Cot(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsinh( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Sinh(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImcosh( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Cosh(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImsech( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Sech(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getImcsch( const OUString& aNum ) +{ + Complex z( aNum ); + + z.Csch(); + + return z.GetString(); +} + +OUString SAL_CALL AnalysisAddIn::getComplex( double fR, double fI, const uno::Any& rSuff ) +{ + bool bi; + + switch( rSuff.getValueTypeClass() ) + { + case uno::TypeClass_VOID: + bi = true; + break; + case uno::TypeClass_STRING: + { + auto pSuff = o3tl::forceAccess<OUString>(rSuff); + bi = *pSuff == "i" || pSuff->isEmpty(); + if( !bi && *pSuff != "j" ) + throw lang::IllegalArgumentException(); + } + break; + default: + throw lang::IllegalArgumentException(); + } + + return Complex( fR, fI, bi ? 'i' : 'j' ).GetString(); +} + +double SAL_CALL AnalysisAddIn::getConvert( double f, const OUString& aFU, const OUString& aTU ) +{ + if( !pCDL ) + pCDL.reset(new ConvertDataList()); + + double fRet = pCDL->Convert( f, aFU, aTU ); + RETURN_FINITE( fRet ); +} + +OUString AnalysisAddIn::AnalysisResId(const char* pResId) +{ + return Translate::get(pResId, aResLocale); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/analysis.hxx b/scaddins/source/analysis/analysis.hxx new file mode 100644 index 000000000..ec82f5094 --- /dev/null +++ b/scaddins/source/analysis/analysis.hxx @@ -0,0 +1,240 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSIS_HXX +#define INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSIS_HXX + + +#include <com/sun/star/sheet/XAddIn.hpp> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sheet/addin/XAnalysis.hpp> +#include <com/sun/star/sheet/XCompatibilityNames.hpp> + +#include <cppuhelper/implbase.hxx> + +#include "analysishelper.hxx" + +#include <memory> + +namespace com::sun::star::lang { class XMultiServiceFactory; } +namespace com::sun::star::sheet { struct LocalizedName; } + +css::uno::Reference< css::uno::XInterface > AnalysisAddIn_CreateInstance( const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + + +class AnalysisAddIn : public cppu::WeakImplHelper< + css::sheet::XAddIn, + css::sheet::XCompatibilityNames, + css::sheet::addin::XAnalysis, + css::lang::XServiceName, + css::lang::XServiceInfo > +{ +private: + css::lang::Locale aFuncLoc; + std::unique_ptr<css::lang::Locale[]> pDefLocales; + std::unique_ptr<sca::analysis::FuncDataList> pFD; + std::unique_ptr<double[]> pFactDoubles; + std::unique_ptr<sca::analysis::ConvertDataList> pCDL; + std::locale aResLocale; + + sca::analysis::ScaAnyConverter aAnyConv; + + /// @throws css::uno::RuntimeException + OUString GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex); + void InitDefLocales(); + inline const css::lang::Locale& GetLocale( sal_uInt32 nInd ); + void InitData(); + + /// Converts an Any to sal_Int32 in the range from 0 to 4 (date calculation mode). + /// + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + sal_Int32 getDateMode( + const css::uno::Reference< css::beans::XPropertySet >& xPropSet, + const css::uno::Any& rAny ); + +public: + explicit AnalysisAddIn( + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + OUString AnalysisResId(const char* pId); + + virtual ~AnalysisAddIn() override; + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + double FactDouble( sal_Int32 nNum ); + + static OUString getImplementationName_Static(); + static css::uno::Sequence< OUString > getSupportedServiceNames_Static(); + + // XAddIn + virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) override; + virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getDisplayArgumentName( const OUString& aProgrammaticFunctionName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getArgumentDescription( const OUString& aProgrammaticFunctionName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getProgrammaticCategoryName( const OUString& aProgrammaticFunctionName ) override; + virtual OUString SAL_CALL getDisplayCategoryName( const OUString& aProgrammaticFunctionName ) override; + + // XCompatibilityNames + virtual css::uno::Sequence< css::sheet::LocalizedName > SAL_CALL getCompatibilityNames( const OUString& aProgrammaticName ) override; + + // XLocalizable + virtual void SAL_CALL setLocale( const css::lang::Locale& eLocale ) override; + virtual css::lang::Locale SAL_CALL getLocale( ) override; + + // XServiceName + virtual OUString SAL_CALL getServiceName( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // methods from own interfaces start here + + // XAnalysis +// virtual double SAL_CALL get_Test( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nMode, double f1, double f2, double f3 ) throw( css::uno::RuntimeException ); + + virtual sal_Int32 SAL_CALL getWorkday( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nDays, const css::uno::Any& aHDay ) override; + virtual double SAL_CALL getYearfrac( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nEndDate, const css::uno::Any& aMode ) override; + virtual sal_Int32 SAL_CALL getEdate( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nMonths ) override; + virtual sal_Int32 SAL_CALL getWeeknum( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nMode ) override; + virtual sal_Int32 SAL_CALL getEomonth( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nMonths ) override; + virtual sal_Int32 SAL_CALL getNetworkdays( const css::uno::Reference< css::beans::XPropertySet >&, sal_Int32 nStartDate, sal_Int32 nEndDate, const css::uno::Any& aHDay ) override; + + virtual sal_Int32 SAL_CALL getIseven( sal_Int32 nVal ) override; + virtual sal_Int32 SAL_CALL getIsodd( sal_Int32 nVal ) override; + + virtual double SAL_CALL getMultinomial( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& aVLst, const css::uno::Sequence< css::uno::Any >& aOptVLst ) override; + virtual double SAL_CALL getSeriessum( double fX, double fN, double fM, const css::uno::Sequence< css::uno::Sequence< double > >& aCoeffList ) override; + virtual double SAL_CALL getQuotient( double fNum, double fDenum ) override; + + virtual double SAL_CALL getMround( double fNum, double fMult ) override; + virtual double SAL_CALL getSqrtpi( double fNum ) override; + + virtual double SAL_CALL getRandbetween( double fMin, double fMax ) override; + + virtual double SAL_CALL getGcd( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< double > >& aVLst, const css::uno::Sequence< css::uno::Any >& aOptVLst ) override; + virtual double SAL_CALL getLcm( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< double > >& aVLst, const css::uno::Sequence< css::uno::Any >& aOptVLst ) override; + + virtual double SAL_CALL getBesseli( double fNum, sal_Int32 nOrder ) override; + virtual double SAL_CALL getBesselj( double fNum, sal_Int32 nOrder ) override; + virtual double SAL_CALL getBesselk( double fNum, sal_Int32 nOrder ) override; + virtual double SAL_CALL getBessely( double fNum, sal_Int32 nOrder ) override; + + virtual OUString SAL_CALL getBin2Oct( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + virtual double SAL_CALL getBin2Dec( const OUString& aNum ) override; + virtual OUString SAL_CALL getBin2Hex( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + + virtual OUString SAL_CALL getOct2Bin( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + virtual double SAL_CALL getOct2Dec( const OUString& aNum ) override; + virtual OUString SAL_CALL getOct2Hex( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + + virtual OUString SAL_CALL getDec2Bin( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 fNum, const css::uno::Any& rPlaces ) override; + virtual OUString SAL_CALL getDec2Oct( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 fNum, const css::uno::Any& rPlaces ) override; + virtual OUString SAL_CALL getDec2Hex( const css::uno::Reference< css::beans::XPropertySet >& xOpt, double fNum, const css::uno::Any& rPlaces ) override; + + virtual OUString SAL_CALL getHex2Bin( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + virtual double SAL_CALL getHex2Dec( const OUString& aNum ) override; + virtual OUString SAL_CALL getHex2Oct( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const OUString& aNum, const css::uno::Any& rPlaces ) override; + + virtual sal_Int32 SAL_CALL getDelta( const css::uno::Reference< css::beans::XPropertySet >& xOpt, double fNum1, const css::uno::Any& rNum2 ) override; + + virtual double SAL_CALL getErf( const css::uno::Reference< css::beans::XPropertySet >& xOpt, double fLowerLimit, const css::uno::Any& rUpperLimit ) override; + virtual double SAL_CALL getErfc( double fLowerLimit ) override; + + virtual sal_Int32 SAL_CALL getGestep( const css::uno::Reference< css::beans::XPropertySet >& xOpt, double fNum, const css::uno::Any& rStep ) override; + + virtual double SAL_CALL getFactdouble( sal_Int32 nNum ) override; + + virtual double SAL_CALL getImabs( const OUString& aNum ) override; + virtual double SAL_CALL getImaginary( const OUString& aNum ) override; + virtual OUString SAL_CALL getImpower( const OUString& aNum, double fPower ) override; + virtual double SAL_CALL getImargument( const OUString& aNum ) override; + virtual OUString SAL_CALL getImcos( const OUString& aNum ) override; + virtual OUString SAL_CALL getImdiv( const OUString& aDivident, const OUString& aDivisor ) override; + virtual OUString SAL_CALL getImexp( const OUString& aNum ) override; + virtual OUString SAL_CALL getImconjugate( const OUString& aNum ) override; + virtual OUString SAL_CALL getImln( const OUString& aNum ) override; + virtual OUString SAL_CALL getImlog10( const OUString& aNum ) override; + virtual OUString SAL_CALL getImlog2( const OUString& aNum ) override; + virtual OUString SAL_CALL getImproduct( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< OUString > >& aNum1, const css::uno::Sequence< css::uno::Any >& aNumList ) override; + virtual double SAL_CALL getImreal( const OUString& aNum ) override; + virtual OUString SAL_CALL getImsin( const OUString& aNum ) override; + virtual OUString SAL_CALL getImsub( const OUString& aNum1, const OUString& aNum2 ) override; + virtual OUString SAL_CALL getImsum( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< OUString > >& aNum1, const css::uno::Sequence< css::uno::Any >& aFollowingPars ) override; + + virtual OUString SAL_CALL getImsqrt( const OUString& aNum ) override; + virtual OUString SAL_CALL getImtan( const OUString& aNum ) override; + virtual OUString SAL_CALL getImsec( const OUString& aNum ) override; + virtual OUString SAL_CALL getImcsc( const OUString& aNum ) override; + virtual OUString SAL_CALL getImcot( const OUString& aNum ) override; + virtual OUString SAL_CALL getImsinh( const OUString& aNum ) override; + virtual OUString SAL_CALL getImcosh( const OUString& aNum ) override; + virtual OUString SAL_CALL getImsech( const OUString& aNum ) override; + virtual OUString SAL_CALL getImcsch( const OUString& aNum ) override; + virtual OUString SAL_CALL getComplex( double fReal, double fImaginary, const css::uno::Any& rSuffix ) override; + + virtual double SAL_CALL getConvert( double fVal, const OUString& aFromUnit, const OUString& aToUnit ) override; + + virtual double SAL_CALL getAmordegrc( const css::uno::Reference< css::beans::XPropertySet >&, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, double fRestVal, double fPer, double fRate, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getAmorlinc( const css::uno::Reference< css::beans::XPropertySet >&, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, double fRestVal, double fPer, double fRate, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getAccrint( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nIssue, sal_Int32 nFirstInter, sal_Int32 nSettle, double fRate, const css::uno::Any& rVal, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getAccrintm( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nIssue, sal_Int32 nSettle, double fRate, const css::uno::Any& rVal, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getReceived( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fInvest, double fDisc, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getDisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fPrice, double fRedemp, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getDuration( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fYield, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getEffect( double fNominal, sal_Int32 nPeriods ) override; + virtual double SAL_CALL getCumprinc( double fRate, sal_Int32 nNumPeriods, double fVal, sal_Int32 nStartPer, sal_Int32 nEndPer, sal_Int32 nPayType ) override; + virtual double SAL_CALL getCumipmt( double fRate, sal_Int32 nNumPeriods, double fVal, sal_Int32 nStartPer, sal_Int32 nEndPer, sal_Int32 nPayType ) override; + virtual double SAL_CALL getPrice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getPricedisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fDisc, double fRedemp, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getPricemat( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, double fRate, double fYield, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getMduration( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fYield, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getNominal( double fRate, sal_Int32 nPeriods ) override; + virtual double SAL_CALL getDollarfr( double fDollarDec, sal_Int32 nFrac ) override; + virtual double SAL_CALL getDollarde( double fDollarFrac, sal_Int32 nFrac ) override; + virtual double SAL_CALL getYield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getYielddisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fPrice, double fRedemp, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getYieldmat( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, double fRate, double fPrice, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getTbilleq( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fDisc ) override; + virtual double SAL_CALL getTbillprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fDisc ) override; + virtual double SAL_CALL getTbillyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fPrice ) override; + virtual double SAL_CALL getOddfprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, sal_Int32 nFirstCoup, double fRate, double fYield, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getOddfyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, sal_Int32 nFirstCoup, double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getOddlprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, double fRate, double fYield, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getOddlyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOptBase) override; + virtual double SAL_CALL getXirr( const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< double > >& rValues, const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rDates, const css::uno::Any& rGuess ) override; + virtual double SAL_CALL getXnpv( double fRate, const css::uno::Sequence< css::uno::Sequence< double > >& rValues, const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rDates ) override; + virtual double SAL_CALL getIntrate( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fInvest, double fRedemp, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCoupncd( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCoupdays( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCoupdaysnc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCoupdaybs( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCouppcd( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getCoupnum( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOptBase ) override; + virtual double SAL_CALL getFvschedule( double fPrinc, const css::uno::Sequence< css::uno::Sequence< double > >& rSchedule ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/analysisdefs.hxx b/scaddins/source/analysis/analysisdefs.hxx new file mode 100644 index 000000000..4ee96be9a --- /dev/null +++ b/scaddins/source/analysis/analysisdefs.hxx @@ -0,0 +1,29 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSISDEFS_HXX +#define INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSISDEFS_HXX + +#define CHK_Freq ( nFreq != 1 && nFreq != 2 && nFreq != 4 ) +#define CHK_FINITE(d) if( !std::isfinite( d ) ) throw css::lang::IllegalArgumentException() +#define RETURN_FINITE(d) if( !std::isfinite( d ) ) throw css::lang::IllegalArgumentException(); return d; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/scaddins/source/analysis/analysishelper.hxx b/scaddins/source/analysis/analysishelper.hxx new file mode 100644 index 000000000..26c9a668e --- /dev/null +++ b/scaddins/source/analysis/analysishelper.hxx @@ -0,0 +1,911 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSISHELPER_HXX +#define INCLUDED_SCADDINS_SOURCE_ANALYSIS_ANALYSISHELPER_HXX + + +#include <com/sun/star/uno/Reference.hxx> + +#include <math.h> + +#include <memory> +#include <vector> + +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::util { class XNumberFormatter2; } + +namespace sca::analysis { + +class ScaAnyConverter; + + +#define PI 3.1415926535897932 + + +inline bool IsLeapYear( sal_uInt16 nYear ); + +#ifdef DISABLE_DYNLOADING + +// Avoid clash with the functions with same name in +// scaddins/source/datefunc/datefunc.cxx. I am not sure if each pair +// have identical semantics, but if yes, one copy should be enough, +// but what would be a suitable library where such functions could go? +// Or can the analysis library depend on the date library or the other +// way around? + +#define DaysInMonth analysishelper_DaysInMonth +#define DateToDays analysishelper_DateToDays +#define DaysToDate analysishelper_DaysToDate +#define GetNullDate analysishelper_GetNullDate + +#endif + +sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear ); +sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear ); +/// @throws css::lang::IllegalArgumentException +void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear ); +/// @throws css::uno::RuntimeException +sal_Int32 GetNullDate( const css::uno::Reference< css::beans::XPropertySet >& xOptions ); +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 ); +inline sal_Int32 GetDiffDate360( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod ); +sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod ); + +sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 ); +inline sal_Int16 GetDayOfWeek( sal_Int32 nDate ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode, + sal_Int32* pOptDaysIn1stYear ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +inline double GetYearFrac( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ); + +double BinomialCoefficient( double n, double k ); +double GetGcd( double f1, double f2 ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double ConvertToDec( const OUString& rFromNum, sal_uInt16 nBaseFrom, sal_uInt16 nCharLim ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +OUString ConvertFromDec( + double fNum, double fMin, double fMax, sal_uInt16 nBase, + sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces ); +double Erf( double fX ); +double Erfc( double fX ); +bool ParseDouble( const sal_Unicode*& rpDoubleAsString, double& rReturn ); +OUString GetString( double fNumber, bool bLeadingSign, sal_uInt16 nMaxNumOfDigits = 15 ); + +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, + double fRestVal, double fPer, double fRate, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, + double fRestVal, double fPer, double fRate, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, + double fYield, sal_Int32 nFreq, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, + double fRate, double fPrice, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +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 ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice, + double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield, + double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +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 ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, + double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, + double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase ); +double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType ); +double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType ); + +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, + sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, + sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, + sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, + sal_Int32 nBase ); + +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, + sal_Int32 nFreq, sal_Int32 nBase ); +/// @throws css::uno::RuntimeException +/// @throws css::lang::IllegalArgumentException +double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, + sal_Int32 nBase ); + + +enum class FDCategory +{ + DateTime, + Finance, + Inf, + Math, + Tech +}; + + +struct FuncDataBase +{ + const char* pIntName; + const char* pUINameID; // resource ID to UI name + const char** pDescrID; // resource ID to description, parameter names and ~ description + bool bDouble; // name already exist in Calc + bool bWithOpt; // first parameter is internal + const char** pCompListID; // list of valid names + sal_uInt16 nNumOfParams; // number of named / described parameters + FDCategory eCat; // function category + const char* pSuffix; // if bDouble, append a suffix other than "_ADD" for UI +}; + + +class FuncData final +{ +private: + OUString aIntName; + const char* pUINameID; + const char** pDescrID; // leads also to parameter descriptions! + bool bDouble; // flag for names that already exist in Calc + bool bWithOpt; // has internal parameter on first position + + sal_uInt16 nParam; // num of parameters + std::vector<OUString> aCompList; // list of all valid names + FDCategory eCat; // function category + OUString aSuffix; // if bDouble and not empty, append a suffix other than "_ADD" for UI + +public: + FuncData(const FuncDataBase& rBaseData); + + inline const char* GetUINameID() const; + inline const char** GetDescrID() const; + inline bool IsDouble() const; + inline const OUString& GetSuffix() const; + + sal_uInt16 GetStrIndex( sal_uInt16 nParamNum ) const; + inline bool Is( const OUString& rCompareTo ) const; + + inline const std::vector<OUString> & + GetCompNameList() const; + + inline FDCategory GetCategory() const; +}; + +typedef std::vector< FuncData > FuncDataList; + +void InitFuncDataList(FuncDataList& rList); + +// Predicate for use with std::find_if +struct FindFuncData +{ + const OUString& m_rId; + explicit FindFuncData( const OUString& rId ) : m_rId(rId) {} + bool operator() ( FuncData const & rCandidate ) const { return rCandidate.Is(m_rId); } +}; + +/// sorted list with unique sal_Int32 values +class SortedIndividualInt32List final +{ +private: + std::vector<sal_Int32> maVector; + + void Insert( sal_Int32 nDay ); + void Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Insert( double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend ); + + /** @param rAnyConv must be an initialized ScaAnyConmverter + @param bInsertOnWeekend insertion mode: false = holidays on weekend are omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void InsertHolidayList( + const ScaAnyConverter& rAnyConv, + const css::uno::Any& rHolAny, + sal_Int32 nNullDate, + bool bInsertOnWeekend ); + +public: + SortedIndividualInt32List(); + ~SortedIndividualInt32List(); + + sal_uInt32 Count() const + { return maVector.size(); } + + /// @return element on position nIndex or 0 on invalid index + sal_Int32 Get( sal_uInt32 n ) const + { return maVector[n]; } + + /// @return true if nVal (internal date representation) is contained + bool Find( sal_Int32 nVal ) const; + + /** @param rAnyConv is an initialized or uninitialized ScaAnyConverter + holidays on weekend are omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void InsertHolidayList( + ScaAnyConverter& rAnyConv, + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + const css::uno::Any& rHolAny, + sal_Int32 nNullDate); +}; + + +class ScaDoubleList +{ +private: + std::vector<double> maVector; +protected: + void ListAppend( double fValue ) { maVector.push_back(fValue); } + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Append( double fValue ) + { if( CheckInsert( fValue ) ) ListAppend( fValue ); } + + /** @param rAnyConv must be an initialized ScaAnyConmverter + @param bIgnoreEmpty handling of empty Any's/strings: false = inserted as 0.0; true = omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void Append( + const ScaAnyConverter& rAnyConv, + const css::uno::Any& rAny, + bool bIgnoreEmpty ); + + /** @param rAnyConv must be an initialized ScaAnyConmverter + @param bIgnoreEmpty handling of empty Any's/strings: false = inserted as 0.0; true = omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void Append( + const ScaAnyConverter& rAnyConv, + const css::uno::Sequence< css::uno::Any >& rAnySeq, + bool bIgnoreEmpty ); + + /** @param rAnyConv must be an initialized ScaAnyConmverter + @param bIgnoreEmpty handling of empty Any's/strings: false = inserted as 0.0; true = omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void Append( + const ScaAnyConverter& rAnyConv, + const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& rAnySeq, + bool bIgnoreEmpty ); + +public: + virtual ~ScaDoubleList() {} + + sal_uInt32 Count() const + { return maVector.size(); } + double Get( sal_uInt32 n ) const + { return maVector[n]; } + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Append( const css::uno::Sequence< css::uno::Sequence< double > >& rValueArr ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Append( const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rValueArr ); + + /** @param rAnyConv is an initialized or uninitialized ScaAnyConverter + @param bIgnoreEmpty handling of empty Any's/strings: false = inserted as 0.0; true = omitted + @throws css::uno::RuntimeException + @throws css::lang::IllegalArgumentException + */ + void Append( + ScaAnyConverter& rAnyConv, + const css::uno::Reference< css::beans::XPropertySet >& xOpt, + const css::uno::Sequence< css::uno::Any >& rAnySeq ); + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + virtual bool CheckInsert( double fValue ) const; +}; + + +/// stores double values >0.0, throws exception for double values <0.0, does nothing for 0.0 +class ScaDoubleListGT0 : public ScaDoubleList +{ +public: + virtual bool CheckInsert( double fValue ) const override; +}; + + +/// stores double values >=0.0, throws exception for double values <0.0 +class ScaDoubleListGE0 : public ScaDoubleList +{ +public: + virtual bool CheckInsert( double fValue ) const override; +}; + + +class Complex +{ + double r; + double i; + sal_Unicode c; + +public: + inline Complex( double fReal, double fImag = 0.0, sal_Unicode cC = '\0' ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + explicit Complex( const OUString& rComplexAsString ); + + inline static bool IsImagUnit( sal_Unicode c ); + static bool ParseString( const OUString& rComplexAsString, Complex& rReturn ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + OUString GetString() const; + + inline double Real() const; + inline double Imag() const; + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + double Arg() const; + inline double Abs() const; + + // following functions change the complex number itself to avoid unnecessary copy actions! + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Power( double fPower ); + void Sqrt(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Sin(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Cos(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Div( const Complex& rDivisor ); + void Exp(); + inline void Conjugate(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Ln(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Log10(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Log2(); + inline void Mult( double fFact ); + inline void Mult( const Complex& rMult ); + inline void Sub( const Complex& rMult ); + inline void Add( const Complex& rAdd ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Tan(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Sec(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Csc(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Cot(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Sinh(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Cosh(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Sech(); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Csch(); + +}; + + +class ComplexList final +{ +private: + std::vector<Complex> maVector; +public: + ~ComplexList(); + + inline const Complex& Get( sal_uInt32 nIndex ) const; + + bool empty() const + { return maVector.empty(); } + sal_uInt32 Count() const + { return maVector.size(); } + + inline void Append( Complex&& pNew ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Append( const css::uno::Sequence< css::uno::Sequence< OUString > >& rComplexNumList ); + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void Append( const css::uno::Sequence< css::uno::Any >& aMultPars ); +}; + + +enum ConvertDataClass +{ + CDC_Mass, CDC_Length, CDC_Time, CDC_Pressure, CDC_Force, CDC_Energy, CDC_Power, CDC_Magnetism, + CDC_Temperature, CDC_Volume, CDC_Area, CDC_Speed, CDC_Information +}; + + +#define INV_MATCHLEV 1764 // guess, what this is... :-) + + +class ConvertData +{ +protected: + friend class ConvertDataList; + double fConst; + OUString aName; + ConvertDataClass eClass; + bool bPrefixSupport; +public: + ConvertData( + const char pUnitName[], + double fConvertConstant, + ConvertDataClass eClass, + bool bPrefSupport = false ); + + virtual ~ConvertData(); + + sal_Int16 GetMatchingLevel( const OUString& rRef ) const; + // 0.0 = no equality + // 1.0 = matches exact + // rest = matches without an assumed prefix of one character + // rest gives power for 10 represented by the prefix (e.g. 3 for k or -9 for n + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + virtual double Convert( double fVal, const ConvertData& rTo, + sal_Int16 nMatchLevelFrom, sal_Int16 nMatchLevelTo ) const; + virtual double ConvertFromBase( double fVal, sal_Int16 nMatchLevel ) const; + + inline ConvertDataClass Class() const; +}; + +class ConvertDataLinear final : public ConvertData +{ + double fOffs; +public: + inline ConvertDataLinear( + const char pUnitName[], + double fConvertConstant, + double fConvertOffset, + ConvertDataClass eClass, + bool bPrefSupport = false ); + + virtual ~ConvertDataLinear() override; + + virtual double Convert( double fVal, const ConvertData& rTo, + sal_Int16 nMatchLevelFrom, sal_Int16 nMatchLevelTo ) const override; + // for cases where f(x) = a + bx applies (e.g. Temperatures) + + // converts fVal from this unit to rFrom unit + // throws exception if not from same class + // this implementation is for proportional cases only + double ConvertToBase( double fVal, sal_Int16 nMatchLevel ) const; + virtual double ConvertFromBase( double fVal, sal_Int16 nMatchLevel ) const override; +}; + + +class ConvertDataList +{ +private: + std::vector<std::unique_ptr<ConvertData>> maVector; +public: + ConvertDataList(); + ~ConvertDataList(); + + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + double Convert( double fVal, const OUString& rFrom, const OUString& rTo ); +}; + + +inline bool IsLeapYear( sal_uInt16 n ) +{ + return ( (( ( n % 4 ) == 0 ) && ( ( n % 100 ) != 0)) || ( ( n % 400 ) == 0 ) ); +} + + +inline sal_Int32 GetDiffDate360( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod ) +{ + return GetDiffDate360( GetNullDate( xOpt ), nDate1, nDate2, bUSAMethod ); +} + + +inline sal_Int16 GetDayOfWeek( sal_Int32 n ) +{ // monday = 0, ..., sunday = 6 + return static_cast< sal_Int16 >( ( n - 1 ) % 7 ); +} + + +inline double GetYearFrac( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode ) +{ + return GetYearFrac( GetNullDate( xOpt ), nStartDate, nEndDate, nMode ); +} + + +inline const char* FuncData::GetUINameID() const +{ + return pUINameID; +} + + +inline const char** FuncData::GetDescrID() const +{ + return pDescrID; +} + + +inline bool FuncData::IsDouble() const +{ + return bDouble; +} + + +inline const OUString& FuncData::GetSuffix() const +{ + return aSuffix; +} + + +inline bool FuncData::Is( const OUString& r ) const +{ + return aIntName == r; +} + + +inline const std::vector<OUString> & FuncData::GetCompNameList() const +{ + return aCompList; +} + + +inline FDCategory FuncData::GetCategory() const +{ + return eCat; +} + + +inline Complex::Complex( double fReal, double fImag, sal_Unicode cC ) : + r( fReal ), i( fImag ), c( cC ) +{ +} + + +inline double Complex::Real() const +{ + return r; +} + + +inline double Complex::Imag() const +{ + return i; +} + + +inline double Complex::Abs() const +{ + return sqrt( r * r + i * i ); +} + + +void Complex::Conjugate() +{ + i = -i; +} + + +inline void Complex::Mult( double f ) +{ + i *= f; + r *= f; +} + + +inline void Complex::Mult( const Complex& rM ) +{ + double r_ = r; + double i_ = i; + + r = r_ * rM.r - i_ * rM.i; + i = r_ * rM.i + i_ * rM.r; + + if( !c ) c = rM.c; +} + + +inline void Complex::Sub( const Complex& rC ) +{ + r -= rC.r; + i -= rC.i; + if( !c ) c = rC.c; +} + + +inline void Complex::Add( const Complex& rAdd ) +{ + r += rAdd.r; + i += rAdd.i; + if( !c ) c = rAdd.c; +} + + +inline const Complex& ComplexList::Get( sal_uInt32 n ) const +{ + return maVector[n]; +} + + +inline void ComplexList::Append( Complex&& p ) +{ + maVector.emplace_back(p); +} + + +inline ConvertDataClass ConvertData::Class() const +{ + return eClass; +} + +inline ConvertDataLinear::ConvertDataLinear( const char p[], double fC, double fO, ConvertDataClass e, + bool bPrefSupport ) : + ConvertData( p, fC, e, bPrefSupport ), + fOffs( fO ) +{ +} + + +/// Helper class for date calculation for various financial functions +class ScaDate +{ +private: + sal_uInt16 nOrigDay; /// is the day of the original date. + sal_uInt16 nDay; /// is the calculated day depending on the current month/year. + sal_uInt16 nMonth; /// is the current month (one-based). + sal_uInt16 nYear; /// is the current year. + bool bLastDayMode : 1; /// if true, recalculate nDay after every calculation. + bool bLastDay : 1; /// is true, if original date was the last day in month. + bool b30Days : 1; /// is true, if every month has 30 days in calculations. + bool bUSMode : 1; /// is true, if the US method of 30-day-calculations is used. + + /// Calculates nDay from nOrigDay and current date. + void setDay(); + + /// @return count of days in current month + inline sal_uInt16 getDaysInMonth() const; + /// @return count of days in given month + inline sal_uInt16 getDaysInMonth( sal_uInt16 _nMon ) const; + + /// @ return count of days in the given month range + sal_Int32 getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const; + /// @ return count of days in the given year range + sal_Int32 getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const; + + /// Adds/subtracts the given count of years, does not adjust day. + /// + /// @throws css::lang::IllegalArgumentException + void doAddYears( sal_Int32 nYearCount ); + +public: + ScaDate(); + /** @param nBase + date handling mode (days in month / days in year): + 0 = 30 days / 360 days (US NASD) + 1 = exact / exact + 2 = exact / 360 + 3 = exact / 365 + 4 = 30 days / 360 days (Europe) + 5 = exact / exact (no last day adjustment) */ + ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase ); + ScaDate( const ScaDate& rCopy ); + ScaDate& operator=( const ScaDate& rCopy ); + + /// @return the current month. + sal_uInt16 getMonth() const { return nMonth; }; + /// @return the current year. + sal_uInt16 getYear() const { return nYear; }; + + /// adds/subtracts the given count of months, adjusts day + /// + /// @throws css::lang::IllegalArgumentException + void addMonths( sal_Int32 nMonthCount ); + + /// sets the given year, adjusts day + inline void setYear( sal_uInt16 nNewYear ); + /// adds/subtracts the given count of years, adjusts day + /// + /// @throws css::lang::IllegalArgumentException + inline void addYears( sal_Int32 nYearCount ); + + /// @return the internal number of the current date + sal_Int32 getDate( sal_Int32 nNullDate ) const; + /// @return the number of days between the two dates + /// + /// @throws css::lang::IllegalArgumentException + static sal_Int32 getDiff( const ScaDate& rFrom, const ScaDate& rTo ); + + bool operator<( const ScaDate& rCmp ) const; + bool operator<=( const ScaDate& rCmp ) const { return !(rCmp < *this); } + bool operator>( const ScaDate& rCmp ) const { return rCmp < *this; } + bool operator>=( const ScaDate& rCmp ) const { return !(*this < rCmp); } +}; + +inline sal_uInt16 ScaDate::getDaysInMonth() const +{ + return getDaysInMonth( nMonth ); +} + +inline sal_uInt16 ScaDate::getDaysInMonth( sal_uInt16 _nMon ) const +{ + return b30Days ? 30 : DaysInMonth( _nMon, nYear ); +} + +inline void ScaDate::setYear( sal_uInt16 nNewYear ) +{ + nYear = nNewYear; + setDay(); +} + +inline void ScaDate::addYears( sal_Int32 nYearCount ) +{ + doAddYears( nYearCount ); + setDay(); +} + + +/// Helper class for Any->double conversion, using current language settings +class ScaAnyConverter +{ +private: + css::uno::Reference< css::util::XNumberFormatter2 > xFormatter; + sal_Int32 nDefaultFormat; + bool bHasValidFormat; + + /** Converts a string to double using the number formatter. If the formatter is not + valid, ::rtl::math::stringToDouble() with english separators will be used. + @throws css::lang::IllegalArgumentException + on strings not representing any double value. + @return the converted double value. */ + double convertToDouble( + const OUString& rString ) const; + +public: + explicit ScaAnyConverter( + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + ~ScaAnyConverter(); + + /// Initializing with current language settings + /// + /// @throws css::uno::RuntimeException + void init( + const css::uno::Reference< css::beans::XPropertySet >& xPropSet ); + + /** Converts an Any to double (without initialization). + The Any can be empty or contain a double or string. + @throws css::lang::IllegalArgumentException + on other Any types or on invalid strings. + @return true if the Any contains a double or a non-empty valid string, + false if the Any is empty or the string is empty */ + bool getDouble( + double& rfResult, + const css::uno::Any& rAny ) const; + + /** Converts an Any to double (with initialization). + The Any can be empty or contain a double or string. + @throws css::lang::IllegalArgumentException + on other Any types or on invalid strings. + @return true if the Any contains a double or a non-empty valid string, + false if the Any is empty or the string is empty */ + bool getDouble( + double& rfResult, + const css::uno::Reference< css::beans::XPropertySet >& xPropSet, + const css::uno::Any& rAny ); + + /** Converts an Any to double (with initialization). + The Any can be empty or contain a double or string. + @throws css::lang::IllegalArgumentException + on other Any types or on invalid strings. + @return the value of the double or string or fDefault if the Any or string is empty */ + double getDouble( + const css::uno::Reference< css::beans::XPropertySet >& xPropSet, + const css::uno::Any& rAny, + double fDefault ); + + /** Converts an Any to sal_Int32 (with initialization). + The Any can be empty or contain a double or string. + @throws css::lang::IllegalArgumentException + on other Any types or on invalid values or strings. + @return true if the Any contains a double or a non-empty valid string, + false if the Any is empty or the string is empty */ + bool getInt32( + sal_Int32& rnResult, + const css::uno::Reference< css::beans::XPropertySet >& xPropSet, + const css::uno::Any& rAny ); + + /** Converts an Any to sal_Int32 (with initialization). + The Any can be empty or contain a double or string. + @throws css::lang::IllegalArgumentException + on other Any types or on invalid values or strings. + @return the truncated value of the double or string or nDefault if the Any or string is empty */ + sal_Int32 getInt32( + const css::uno::Reference< css::beans::XPropertySet >& xPropSet, + const css::uno::Any& rAny, + sal_Int32 nDefault ); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/bessel.cxx b/scaddins/source/analysis/bessel.cxx new file mode 100644 index 000000000..bb6023a4f --- /dev/null +++ b/scaddins/source/analysis/bessel.cxx @@ -0,0 +1,456 @@ +/* -*- 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 "bessel.hxx" + +#include <rtl/math.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/sheet/NoConvergenceException.hpp> + +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::sheet::NoConvergenceException; + +namespace sca::analysis { + +const double f_PI = 3.1415926535897932385; +const double f_PI_DIV_2 = f_PI / 2.0; +const double f_PI_DIV_4 = f_PI / 4.0; +const double f_2_DIV_PI = 2.0 / f_PI; + + +// BESSEL J + + +/* The BESSEL function, first kind, unmodified: + The algorithm follows + http://www.reference-global.com/isbn/978-3-11-020354-7 + Numerical Mathematics 1 / Numerische Mathematik 1, + An algorithm-based introduction / Eine algorithmisch orientierte Einfuehrung + Deuflhard, Peter; Hohmann, Andreas + Berlin, New York (Walter de Gruyter) 2008 + 4. ueberarb. u. erw. Aufl. 2008 + eBook ISBN: 978-3-11-020355-4 + Chapter 6.3.2 , algorithm 6.24 + The source is in German. + The BesselJ-function is a special case of the adjoint summation with + a_k = 2*(k-1)/x for k=1,... + b_k = -1, for all k, directly substituted + m_0=1, m_k=2 for k even, and m_k=0 for k odd, calculated on the fly + alpha_k=1 for k=N and alpha_k=0 otherwise +*/ + +double BesselJ( double x, sal_Int32 N ) + +{ + if( N < 0 ) + throw IllegalArgumentException(); + if (x==0.0) + return (N==0) ? 1.0 : 0.0; + + /* The algorithm works only for x>0, therefore remember sign. BesselJ + with integer order N is an even function for even N (means J(-x)=J(x)) + and an odd function for odd N (means J(-x)=-J(x)).*/ + double fSign = (N % 2 == 1 && x < 0) ? -1.0 : 1.0; + double fX = fabs(x); + + const double fMaxIteration = 9000000.0; //experimental, for to return in < 3 seconds + double fEstimateIteration = fX * 1.5 + N; + bool bAsymptoticPossible = pow(fX,0.4) > N; + if (fEstimateIteration > fMaxIteration) + { + if (!bAsymptoticPossible) + throw NoConvergenceException(); + return fSign * sqrt(f_2_DIV_PI/fX)* cos(fX-N*f_PI_DIV_2-f_PI_DIV_4); + } + + double const epsilon = 1.0e-15; // relative error + bool bHasfound = false; + double k= 0.0; + // e_{-1} = 0; e_0 = alpha_0 / b_2 + double u ; // u_0 = e_0/f_0 = alpha_0/m_0 = alpha_0 + + // first used with k=1 + double m_bar; // m_bar_k = m_k * f_bar_{k-1} + double g_bar; // g_bar_k = m_bar_k - a_{k+1} + g_{k-1} + double g_bar_delta_u; // g_bar_delta_u_k = f_bar_{k-1} * alpha_k + // - g_{k-1} * delta_u_{k-1} - m_bar_k * u_{k-1} + // f_{-1} = 0.0; f_0 = m_0 / b_2 = 1/(-1) = -1 + double g = 0.0; // g_0= f_{-1} / f_0 = 0/(-1) = 0 + double delta_u = 0.0; // dummy initialize, first used with * 0 + double f_bar = -1.0; // f_bar_k = 1/f_k, but only used for k=0 + + if (N==0) + { + //k=0; alpha_0 = 1.0 + u = 1.0; // u_0 = alpha_0 + // k = 1.0; at least one step is necessary + // m_bar_k = m_k * f_bar_{k-1} ==> m_bar_1 = 0.0 + g_bar_delta_u = 0.0; // alpha_k = 0.0, m_bar = 0.0; g= 0.0 + g_bar = - 2.0/fX; // k = 1.0, g = 0.0 + delta_u = g_bar_delta_u / g_bar; + u = u + delta_u ; // u_k = u_{k-1} + delta_u_k + g = -1.0 / g_bar; // g_k=b_{k+2}/g_bar_k + f_bar = f_bar * g; // f_bar_k = f_bar_{k-1}* g_k + k = 2.0; + // From now on all alpha_k = 0.0 and k > N+1 + } + else + { // N >= 1 and alpha_k = 0.0 for k<N + u=0.0; // u_0 = alpha_0 + for (k =1.0; k<= N-1; k = k + 1.0) + { + m_bar=2.0 * fmod(k-1.0, 2.0) * f_bar; + g_bar_delta_u = - g * delta_u - m_bar * u; // alpha_k = 0.0 + g_bar = m_bar - 2.0*k/fX + g; + delta_u = g_bar_delta_u / g_bar; + u = u + delta_u; + g = -1.0/g_bar; + f_bar=f_bar * g; + } + // Step alpha_N = 1.0 + m_bar=2.0 * fmod(k-1.0, 2.0) * f_bar; + g_bar_delta_u = f_bar - g * delta_u - m_bar * u; // alpha_k = 1.0 + g_bar = m_bar - 2.0*k/fX + g; + delta_u = g_bar_delta_u / g_bar; + u = u + delta_u; + g = -1.0/g_bar; + f_bar = f_bar * g; + k = k + 1.0; + } + // Loop until desired accuracy, always alpha_k = 0.0 + do + { + m_bar = 2.0 * fmod(k-1.0, 2.0) * f_bar; + g_bar_delta_u = - g * delta_u - m_bar * u; + g_bar = m_bar - 2.0*k/fX + g; + delta_u = g_bar_delta_u / g_bar; + u = u + delta_u; + g = -1.0/g_bar; + f_bar = f_bar * g; + bHasfound = (fabs(delta_u)<=fabs(u)*epsilon); + k = k + 1.0; + } + while (!bHasfound && k <= fMaxIteration); + if (!bHasfound) + throw NoConvergenceException(); // unlikely to happen + + return u * fSign; +} + + +// BESSEL I + + +/* The BESSEL function, first kind, modified: + + inf (x/2)^(n+2k) + I_n(x) = SUM TERM(n,k) with TERM(n,k) := -------------- + k=0 k! (n+k)! + + No asymptotic approximation used, see issue 43040. + */ + +double BesselI( double x, sal_Int32 n ) +{ + const sal_Int32 nMaxIteration = 2000; + const double fXHalf = x / 2.0; + if( n < 0 ) + throw IllegalArgumentException(); + + double fResult = 0.0; + + /* Start the iteration without TERM(n,0), which is set here. + + TERM(n,0) = (x/2)^n / n! + */ + sal_Int32 nK = 0; + double fTerm = 1.0; + // avoid overflow in Fak(n) + for( nK = 1; nK <= n; ++nK ) + { + fTerm = fTerm / static_cast< double >( nK ) * fXHalf; + } + fResult = fTerm; // Start result with TERM(n,0). + if( fTerm != 0.0 ) + { + nK = 1; + const double fEpsilon = 1.0E-15; + do + { + /* Calculation of TERM(n,k) from TERM(n,k-1): + + (x/2)^(n+2k) + TERM(n,k) = -------------- + k! (n+k)! + + (x/2)^2 (x/2)^(n+2(k-1)) + = -------------------------- + k (k-1)! (n+k) (n+k-1)! + + (x/2)^2 (x/2)^(n+2(k-1)) + = --------- * ------------------ + k(n+k) (k-1)! (n+k-1)! + + x^2/4 + = -------- TERM(n,k-1) + k(n+k) + */ + fTerm = fTerm * fXHalf / static_cast<double>(nK) * fXHalf / static_cast<double>(nK+n); + fResult += fTerm; + nK++; + } + while( (fabs( fTerm ) > fabs(fResult) * fEpsilon) && (nK < nMaxIteration) ); + + } + return fResult; +} + +/// @throws IllegalArgumentException +/// @throws NoConvergenceException +static double Besselk0( double fNum ) +{ + double fRet; + + if( fNum <= 2.0 ) + { + double fNum2 = fNum * 0.5; + double y = fNum2 * fNum2; + + fRet = -log( fNum2 ) * BesselI( fNum, 0 ) + + ( -0.57721566 + y * ( 0.42278420 + y * ( 0.23069756 + y * ( 0.3488590e-1 + + y * ( 0.262698e-2 + y * ( 0.10750e-3 + y * 0.74e-5 ) ) ) ) ) ); + } + else + { + double y = 2.0 / fNum; + + fRet = exp( -fNum ) / sqrt( fNum ) * ( 1.25331414 + y * ( -0.7832358e-1 + + y * ( 0.2189568e-1 + y * ( -0.1062446e-1 + y * ( 0.587872e-2 + + y * ( -0.251540e-2 + y * 0.53208e-3 ) ) ) ) ) ); + } + + return fRet; +} + +/// @throws IllegalArgumentException +/// @throws NoConvergenceException +static double Besselk1( double fNum ) +{ + double fRet; + + if( fNum <= 2.0 ) + { + double fNum2 = fNum * 0.5; + double y = fNum2 * fNum2; + + fRet = log( fNum2 ) * BesselI( fNum, 1 ) + + ( 1.0 + y * ( 0.15443144 + y * ( -0.67278579 + y * ( -0.18156897 + y * ( -0.1919402e-1 + + y * ( -0.110404e-2 + y * -0.4686e-4 ) ) ) ) ) ) + / fNum; + } + else + { + double y = 2.0 / fNum; + + fRet = exp( -fNum ) / sqrt( fNum ) * ( 1.25331414 + y * ( 0.23498619 + + y * ( -0.3655620e-1 + y * ( 0.1504268e-1 + y * ( -0.780353e-2 + + y * ( 0.325614e-2 + y * -0.68245e-3 ) ) ) ) ) ); + } + + return fRet; +} + + +double BesselK( double fNum, sal_Int32 nOrder ) +{ + switch( nOrder ) + { + case 0: return Besselk0( fNum ); + case 1: return Besselk1( fNum ); + default: + { + double fTox = 2.0 / fNum; + double fBkm = Besselk0( fNum ); + double fBk = Besselk1( fNum ); + + for( sal_Int32 n = 1 ; n < nOrder ; n++ ) + { + const double fBkp = fBkm + double( n ) * fTox * fBk; + fBkm = fBk; + fBk = fBkp; + } + + return fBk; + } + } +} + + +// BESSEL Y + + +/* The BESSEL function, second kind, unmodified: + The algorithm for order 0 and for order 1 follows + http://www.reference-global.com/isbn/978-3-11-020354-7 + Numerical Mathematics 1 / Numerische Mathematik 1, + An algorithm-based introduction / Eine algorithmisch orientierte Einfuehrung + Deuflhard, Peter; Hohmann, Andreas + Berlin, New York (Walter de Gruyter) 2008 + 4. ueberarb. u. erw. Aufl. 2008 + eBook ISBN: 978-3-11-020355-4 + Chapter 6.3.2 , algorithm 6.24 + The source is in German. + See #i31656# for a commented version of the implementation, attachment #desc6 + https://bz.apache.org/ooo/attachment.cgi?id=63609 +*/ + +/// @throws IllegalArgumentException +/// @throws NoConvergenceException +static double Bessely0( double fX ) +{ + if (fX <= 0) + throw IllegalArgumentException(); + const double fMaxIteration = 9000000.0; // should not be reached + if (fX > 5.0e+6) // iteration is not considerable better then approximation + return sqrt(1/f_PI/fX) + *(rtl::math::sin(fX)-rtl::math::cos(fX)); + const double epsilon = 1.0e-15; + const double EulerGamma = 0.57721566490153286060; + double alpha = log(fX/2.0)+EulerGamma; + double u = alpha; + + double k = 1.0; + double g_bar_delta_u = 0.0; + double g_bar = -2.0 / fX; + double delta_u = g_bar_delta_u / g_bar; + double g = -1.0/g_bar; + double f_bar = -1 * g; + + double sign_alpha = 1.0; + bool bHasFound = false; + k = k + 1; + do + { + double km1mod2 = fmod(k-1.0, 2.0); + double m_bar = (2.0*km1mod2) * f_bar; + if (km1mod2 == 0.0) + alpha = 0.0; + else + { + alpha = sign_alpha * (4.0/k); + sign_alpha = -sign_alpha; + } + g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u; + g_bar = m_bar - (2.0*k)/fX + g; + delta_u = g_bar_delta_u / g_bar; + u = u+delta_u; + g = -1.0 / g_bar; + f_bar = f_bar*g; + bHasFound = (fabs(delta_u)<=fabs(u)*epsilon); + k=k+1; + } + while (!bHasFound && k<fMaxIteration); + if (!bHasFound) + throw NoConvergenceException(); // not likely to happen + return u*f_2_DIV_PI; +} + +// See #i31656# for a commented version of this implementation, attachment #desc6 +// https://bz.apache.org/ooo/attachment.cgi?id=63609 +/// @throws IllegalArgumentException +/// @throws NoConvergenceException +static double Bessely1( double fX ) +{ + if (fX <= 0) + throw IllegalArgumentException(); + const double fMaxIteration = 9000000.0; // should not be reached + if (fX > 5.0e+6) // iteration is not considerable better then approximation + return - sqrt(1/f_PI/fX) + *(rtl::math::sin(fX)+rtl::math::cos(fX)); + const double epsilon = 1.0e-15; + const double EulerGamma = 0.57721566490153286060; + double alpha = 1.0/fX; + double f_bar = -1.0; + double u = alpha; + double k = 1.0; + alpha = 1.0 - EulerGamma - log(fX/2.0); + double g_bar_delta_u = -alpha; + double g_bar = -2.0 / fX; + double delta_u = g_bar_delta_u / g_bar; + u = u + delta_u; + double g = -1.0/g_bar; + f_bar = f_bar * g; + double sign_alpha = -1.0; + bool bHasFound = false; + k = k + 1.0; + do + { + double km1mod2 = fmod(k-1.0,2.0); + double m_bar = (2.0*km1mod2) * f_bar; + double q = (k-1.0)/2.0; + if (km1mod2 == 0.0) // k is odd + { + alpha = sign_alpha * (1.0/q + 1.0/(q+1.0)); + sign_alpha = -sign_alpha; + } + else + alpha = 0.0; + g_bar_delta_u = f_bar * alpha - g * delta_u - m_bar * u; + g_bar = m_bar - (2.0*k)/fX + g; + delta_u = g_bar_delta_u / g_bar; + u = u+delta_u; + g = -1.0 / g_bar; + f_bar = f_bar*g; + bHasFound = (fabs(delta_u)<=fabs(u)*epsilon); + k=k+1; + } + while (!bHasFound && k<fMaxIteration); + if (!bHasFound) + throw NoConvergenceException(); + return -u*2.0/f_PI; +} + +double BesselY( double fNum, sal_Int32 nOrder ) +{ + switch( nOrder ) + { + case 0: return Bessely0( fNum ); + case 1: return Bessely1( fNum ); + default: + { + double fTox = 2.0 / fNum; + double fBym = Bessely0( fNum ); + double fBy = Bessely1( fNum ); + + for( sal_Int32 n = 1 ; n < nOrder ; n++ ) + { + const double fByp = double( n ) * fTox * fBy - fBym; + fBym = fBy; + fBy = fByp; + } + + return fBy; + } + } +} + +} // namespace sca::analysis + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/bessel.hxx b/scaddins/source/analysis/bessel.hxx new file mode 100644 index 000000000..7aea95707 --- /dev/null +++ b/scaddins/source/analysis/bessel.hxx @@ -0,0 +1,65 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SCADDINS_SOURCE_ANALYSIS_BESSEL_HXX +#define INCLUDED_SCADDINS_SOURCE_ANALYSIS_BESSEL_HXX + +#include <sal/types.h> + +namespace sca { +namespace analysis { + + +// BESSEL functions + + +/** Returns the result for the unmodified BESSEL function of first kind (J), n-th order, at point x. + + @throws css::lang::IllegalArgumentException + @throws css::sheet::NoConvergenceException +*/ +double BesselJ( double x, sal_Int32 n ); + +/** Returns the result for the modified BESSEL function of first kind (I), n-th order, at point x. + + @throws css::lang::IllegalArgumentException + @throws css::sheet::NoConvergenceException +*/ +double BesselI( double x, sal_Int32 n ); + +/** Returns the result for the unmodified BESSEL function of second kind (Y), n-th order, at point x. + + @throws css::lang::IllegalArgumentException + @throws css::sheet::NoConvergenceException +*/ +double BesselY( double x, sal_Int32 n ); + +/** Returns the result for the modified BESSEL function of second kind (K), n-th order, at point x. + + @throws css::lang::IllegalArgumentException + @throws css::sheet::NoConvergenceException +*/ +double BesselK( double x, sal_Int32 n ); + +} // namespace analysis +} // namespace sca + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/deffuncname.hxx b/scaddins/source/analysis/deffuncname.hxx new file mode 100644 index 000000000..ee193e21e --- /dev/null +++ b/scaddins/source/analysis/deffuncname.hxx @@ -0,0 +1,631 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SCADDINS_SOURCE_ANALYSIS_DEFFUNCTIONNAME_HXX +#define INCLUDED_SCADDINS_SOURCE_ANALYSIS_DEFFUNCTIONNAME_HXX + +static const char* ANALYSIS_DEFFUNCNAME_Workday[2] = +{ + reinterpret_cast<char const *>(u8"ARBEITSTAG"), + reinterpret_cast<char const *>(u8"WORKDAY") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Yearfrac[2] = +{ + reinterpret_cast<char const *>(u8"BRTEILJAHRE"), + reinterpret_cast<char const *>(u8"YEARFRAC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Edate[2] = +{ + reinterpret_cast<char const *>(u8"EDATUM"), + reinterpret_cast<char const *>(u8"EDATE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Weeknum[2] = +{ + reinterpret_cast<char const *>(u8"KALENDERWOCHE"), + reinterpret_cast<char const *>(u8"WEEKNUM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Eomonth[2] = +{ + reinterpret_cast<char const *>(u8"MONATSENDE"), + reinterpret_cast<char const *>(u8"EOMONTH") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Networkdays[2] = +{ + reinterpret_cast<char const *>(u8"NETTOARBEITSTAGE"), + reinterpret_cast<char const *>(u8"NETWORKDAYS") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Amordegrc[2] = +{ + reinterpret_cast<char const *>(u8"AMORDEGRK"), + reinterpret_cast<char const *>(u8"AMORDEGRC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Amorlinc[2] = +{ + reinterpret_cast<char const *>(u8"AMORLINEARK"), + reinterpret_cast<char const *>(u8"AMORLINC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Accrint[2] = +{ + reinterpret_cast<char const *>(u8"AUFGELZINS"), + reinterpret_cast<char const *>(u8"ACCRINT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Accrintm[2] = +{ + reinterpret_cast<char const *>(u8"AUFGELZINSF"), + reinterpret_cast<char const *>(u8"ACCRINTM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Received[2] = +{ + reinterpret_cast<char const *>(u8"AUSZAHLUNG"), + reinterpret_cast<char const *>(u8"RECEIVED") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Disc[2] = +{ + reinterpret_cast<char const *>(u8"DISAGIO"), + reinterpret_cast<char const *>(u8"DISC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Duration[2] = +{ + reinterpret_cast<char const *>(u8"DURATION"), + reinterpret_cast<char const *>(u8"DURATION") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Effect[2] = +{ + reinterpret_cast<char const *>(u8"EFFEKTIV"), + reinterpret_cast<char const *>(u8"EFFECT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Cumprinc[2] = +{ + reinterpret_cast<char const *>(u8"KUMKAPITAL"), + reinterpret_cast<char const *>(u8"CUMPRINC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Cumipmt[2] = +{ + reinterpret_cast<char const *>(u8"KUMZINSZ"), + reinterpret_cast<char const *>(u8"CUMIPMT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Price[2] = +{ + reinterpret_cast<char const *>(u8"KURS"), + reinterpret_cast<char const *>(u8"PRICE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Pricedisc[2] = +{ + reinterpret_cast<char const *>(u8"KURSDISAGIO"), + reinterpret_cast<char const *>(u8"PRICEDISC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Pricemat[2] = +{ + reinterpret_cast<char const *>(u8"KURSFÄLLIG"), + reinterpret_cast<char const *>(u8"PRICEMAT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Mduration[2] = +{ + reinterpret_cast<char const *>(u8"MDURATION"), + reinterpret_cast<char const *>(u8"MDURATION") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Nominal[2] = +{ + reinterpret_cast<char const *>(u8"NOMINAL"), + reinterpret_cast<char const *>(u8"NOMINAL") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Dollarfr[2] = +{ + reinterpret_cast<char const *>(u8"NOTIERUNGBRU"), + reinterpret_cast<char const *>(u8"DOLLARFR") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Dollarde[2] = +{ + reinterpret_cast<char const *>(u8"NOTIERUNGDEZ"), + reinterpret_cast<char const *>(u8"DOLLARDE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Yield[2] = +{ + reinterpret_cast<char const *>(u8"RENDITE"), + reinterpret_cast<char const *>(u8"YIELD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Yielddisc[2] = +{ + reinterpret_cast<char const *>(u8"RENDITEDIS"), + reinterpret_cast<char const *>(u8"YIELDDISC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Yieldmat[2] = +{ + reinterpret_cast<char const *>(u8"RENDITEFÄLL"), + reinterpret_cast<char const *>(u8"YIELDMAT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Tbilleq[2] = +{ + reinterpret_cast<char const *>(u8"TBILLÄQUIV"), + reinterpret_cast<char const *>(u8"TBILLEQ") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Tbillprice[2] = +{ + reinterpret_cast<char const *>(u8"TBILLKURS"), + reinterpret_cast<char const *>(u8"TBILLPRICE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Tbillyield[2] = +{ + reinterpret_cast<char const *>(u8"TBILLRENDITE"), + reinterpret_cast<char const *>(u8"TBILLYIELD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oddfprice[2] = +{ + reinterpret_cast<char const *>(u8"UNREGER.KURS"), + reinterpret_cast<char const *>(u8"ODDFPRICE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oddfyield[2] = +{ + reinterpret_cast<char const *>(u8"UNREGER.REND"), + reinterpret_cast<char const *>(u8"ODDFYIELD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oddlprice[2] = +{ + reinterpret_cast<char const *>(u8"UNREGLE.KURS"), + reinterpret_cast<char const *>(u8"ODDLPRICE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oddlyield[2] = +{ + reinterpret_cast<char const *>(u8"UNREGLE.REND"), + reinterpret_cast<char const *>(u8"ODDLYIELD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Xirr[2] = +{ + reinterpret_cast<char const *>(u8"XINTZINSFUSS"), + reinterpret_cast<char const *>(u8"XIRR") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Xnpv[2] = +{ + reinterpret_cast<char const *>(u8"XKAPITALWERT"), + reinterpret_cast<char const *>(u8"XNPV") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Intrate[2] = +{ + reinterpret_cast<char const *>(u8"ZINSSATZ"), + reinterpret_cast<char const *>(u8"INTRATE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Coupncd[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMNZ"), + reinterpret_cast<char const *>(u8"COUPNCD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Coupdays[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMTAGE"), + reinterpret_cast<char const *>(u8"COUPDAYS") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Coupdaysnc[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMTAGNZ"), + reinterpret_cast<char const *>(u8"COUPDAYSNC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Coupdaybs[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMTAGVA"), + reinterpret_cast<char const *>(u8"COUPDAYBS") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Couppcd[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMVZ"), + reinterpret_cast<char const *>(u8"COUPPCD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Coupnum[2] = +{ + reinterpret_cast<char const *>(u8"ZINSTERMZAHL"), + reinterpret_cast<char const *>(u8"COUPNUM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Fvschedule[2] = +{ + reinterpret_cast<char const *>(u8"ZW2"), + reinterpret_cast<char const *>(u8"FVSCHEDULE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Iseven[2] = +{ + reinterpret_cast<char const *>(u8"ISTGERADE"), + reinterpret_cast<char const *>(u8"ISEVEN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Isodd[2] = +{ + reinterpret_cast<char const *>(u8"ISTUNGERADE"), + reinterpret_cast<char const *>(u8"ISODD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Gcd[2] = +{ + reinterpret_cast<char const *>(u8"GGT"), + reinterpret_cast<char const *>(u8"GCD") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Lcm[2] = +{ + reinterpret_cast<char const *>(u8"KGV"), + reinterpret_cast<char const *>(u8"LCM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Multinomial[2] = +{ + reinterpret_cast<char const *>(u8"POLYNOMIAL"), + reinterpret_cast<char const *>(u8"MULTINOMIAL") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Seriessum[2] = +{ + reinterpret_cast<char const *>(u8"POTENZREIHE"), + reinterpret_cast<char const *>(u8"SERIESSUM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Quotient[2] = +{ + reinterpret_cast<char const *>(u8"QUOTIENT"), + reinterpret_cast<char const *>(u8"QUOTIENT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Mround[2] = +{ + reinterpret_cast<char const *>(u8"VRUNDEN"), + reinterpret_cast<char const *>(u8"MROUND") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Sqrtpi[2] = +{ + reinterpret_cast<char const *>(u8"WURZELPI"), + reinterpret_cast<char const *>(u8"SQRTPI") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Randbetween[2] = +{ + reinterpret_cast<char const *>(u8"ZUFALLSBEREICH"), + reinterpret_cast<char const *>(u8"RANDBETWEEN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Besseli[2] = +{ + reinterpret_cast<char const *>(u8"BESSELI"), + reinterpret_cast<char const *>(u8"BESSELI") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Besselj[2] = +{ + reinterpret_cast<char const *>(u8"BESSELJ"), + reinterpret_cast<char const *>(u8"BESSELJ") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Besselk[2] = +{ + reinterpret_cast<char const *>(u8"BESSELK"), + reinterpret_cast<char const *>(u8"BESSELK") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Bessely[2] = +{ + reinterpret_cast<char const *>(u8"BESSELY"), + reinterpret_cast<char const *>(u8"BESSELY") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Bin2Dec[2] = +{ + reinterpret_cast<char const *>(u8"BININDEZ"), + reinterpret_cast<char const *>(u8"BIN2DEC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Bin2Hex[2] = +{ + reinterpret_cast<char const *>(u8"BININHEX"), + reinterpret_cast<char const *>(u8"BIN2HEX") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Bin2Oct[2] = +{ + reinterpret_cast<char const *>(u8"BININOKT"), + reinterpret_cast<char const *>(u8"BIN2OCT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Delta[2] = +{ + reinterpret_cast<char const *>(u8"DELTA"), + reinterpret_cast<char const *>(u8"DELTA") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Dec2Bin[2] = +{ + reinterpret_cast<char const *>(u8"DEZINBIN"), + reinterpret_cast<char const *>(u8"DEC2BIN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Dec2Hex[2] = +{ + reinterpret_cast<char const *>(u8"DEZINHEX"), + reinterpret_cast<char const *>(u8"DEC2HEX") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Dec2Oct[2] = +{ + reinterpret_cast<char const *>(u8"DEZINOKT"), + reinterpret_cast<char const *>(u8"DEC2OCT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Erf[2] = +{ + reinterpret_cast<char const *>(u8"GAUSSFEHLER"), + reinterpret_cast<char const *>(u8"ERF") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Erfc[2] = +{ + reinterpret_cast<char const *>(u8"GAUSSFKOMPL"), + reinterpret_cast<char const *>(u8"ERFC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Gestep[2] = +{ + reinterpret_cast<char const *>(u8"GGANZZAHL"), + reinterpret_cast<char const *>(u8"GESTEP") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Hex2Bin[2] = +{ + reinterpret_cast<char const *>(u8"HEXINBIN"), + reinterpret_cast<char const *>(u8"HEX2BIN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Hex2Dec[2] = +{ + reinterpret_cast<char const *>(u8"HEXINDEZ"), + reinterpret_cast<char const *>(u8"HEX2DEC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Hex2Oct[2] = +{ + reinterpret_cast<char const *>(u8"HEXINOKT"), + reinterpret_cast<char const *>(u8"HEX2OCT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imabs[2] = +{ + reinterpret_cast<char const *>(u8"IMABS"), + reinterpret_cast<char const *>(u8"IMABS") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imaginary[2] = +{ + reinterpret_cast<char const *>(u8"IMAGINÄRTEIL"), + reinterpret_cast<char const *>(u8"IMAGINARY") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Impower[2] = +{ + reinterpret_cast<char const *>(u8"IMAPOTENZ"), + reinterpret_cast<char const *>(u8"IMPOWER") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imargument[2] = +{ + reinterpret_cast<char const *>(u8"IMARGUMENT"), + reinterpret_cast<char const *>(u8"IMARGUMENT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imcos[2] = +{ + reinterpret_cast<char const *>(u8"IMCOS"), + reinterpret_cast<char const *>(u8"IMCOS") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imdiv[2] = +{ + reinterpret_cast<char const *>(u8"IMDIV"), + reinterpret_cast<char const *>(u8"IMDIV") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imexp[2] = +{ + reinterpret_cast<char const *>(u8"IMEXP"), + reinterpret_cast<char const *>(u8"IMEXP") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imconjugate[2] = +{ + reinterpret_cast<char const *>(u8"IMKONJUGIERTE"), + reinterpret_cast<char const *>(u8"IMCONJUGATE") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imln[2] = +{ + reinterpret_cast<char const *>(u8"IMLN"), + reinterpret_cast<char const *>(u8"IMLN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imlog10[2] = +{ + reinterpret_cast<char const *>(u8"IMLOG10"), + reinterpret_cast<char const *>(u8"IMLOG10") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imlog2[2] = +{ + reinterpret_cast<char const *>(u8"IMLOG2"), + reinterpret_cast<char const *>(u8"IMLOG2") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Improduct[2] = +{ + reinterpret_cast<char const *>(u8"IMPRODUKT"), + reinterpret_cast<char const *>(u8"IMPRODUCT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imreal[2] = +{ + reinterpret_cast<char const *>(u8"IMREALTEIL"), + reinterpret_cast<char const *>(u8"IMREAL") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsin[2] = +{ + reinterpret_cast<char const *>(u8"IMSIN"), + reinterpret_cast<char const *>(u8"IMSIN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsub[2] = +{ + reinterpret_cast<char const *>(u8"IMSUB"), + reinterpret_cast<char const *>(u8"IMSUB") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsum[2] = +{ + reinterpret_cast<char const *>(u8"IMSUMME"), + reinterpret_cast<char const *>(u8"IMSUM") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsqrt[2] = +{ + reinterpret_cast<char const *>(u8"IMWURZEL"), + reinterpret_cast<char const *>(u8"IMSQRT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imtan[2] = +{ + reinterpret_cast<char const *>(u8"IMTAN"), + reinterpret_cast<char const *>(u8"IMTAN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsec[2] = +{ + reinterpret_cast<char const *>(u8"IMSEC"), + reinterpret_cast<char const *>(u8"IMSEC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imcsc[2] = +{ + reinterpret_cast<char const *>(u8"IMCSC"), + reinterpret_cast<char const *>(u8"IMCSC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imcot[2] = +{ + reinterpret_cast<char const *>(u8"IMCOT"), + reinterpret_cast<char const *>(u8"IMCOT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsinh[2] = +{ + reinterpret_cast<char const *>(u8"IMSINH"), + reinterpret_cast<char const *>(u8"IMSINH") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imcosh[2] = +{ + reinterpret_cast<char const *>(u8"IMCOSH"), + reinterpret_cast<char const *>(u8"IMCOSH") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imsech[2] = +{ + reinterpret_cast<char const *>(u8"IMSECH"), + reinterpret_cast<char const *>(u8"IMSECH") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Imcsch[2] = +{ + reinterpret_cast<char const *>(u8"IMCSCH"), + reinterpret_cast<char const *>(u8"IMCSCH") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Complex[2] = +{ + reinterpret_cast<char const *>(u8"KOMPLEXE"), + reinterpret_cast<char const *>(u8"COMPLEX") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oct2Bin[2] = +{ + reinterpret_cast<char const *>(u8"OKTINBIN"), + reinterpret_cast<char const *>(u8"OCT2BIN") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oct2Dec[2] = +{ + reinterpret_cast<char const *>(u8"OKTINDEZ"), + reinterpret_cast<char const *>(u8"OCT2DEC") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Oct2Hex[2] = +{ + reinterpret_cast<char const *>(u8"OKTINHEX"), + reinterpret_cast<char const *>(u8"OCT2HEX") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Convert[2] = +{ + reinterpret_cast<char const *>(u8"UMWANDELN"), + reinterpret_cast<char const *>(u8"CONVERT") +}; + +static const char* ANALYSIS_DEFFUNCNAME_Factdouble[2] = +{ + reinterpret_cast<char const *>(u8"ZWEIFAKULTÄT"), + reinterpret_cast<char const *>(u8"FACTDOUBLE") +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/analysis/financial.cxx b/scaddins/source/analysis/financial.cxx new file mode 100644 index 000000000..cf41b268e --- /dev/null +++ b/scaddins/source/analysis/financial.cxx @@ -0,0 +1,676 @@ +/* -*- 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 "analysisdefs.hxx" +#include "analysis.hxx" +#include "analysishelper.hxx" +#include <o3tl/temporary.hxx> +#include <rtl/math.hxx> + +using namespace sca::analysis; + +double SAL_CALL AnalysisAddIn::getAmordegrc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, double fRestVal, + double fPer, double fRate, const css::uno::Any& rOB ) +{ + if( nDate > nFirstPer || fRate <= 0.0 || fRestVal > fCost || + fCost <= 0.0 || fRestVal < 0 || fPer < 0 ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetAmordegrc( GetNullDate( xOpt ), fCost, nDate, nFirstPer, fRestVal, fPer, fRate, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getAmorlinc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + double fCost, sal_Int32 nDate, sal_Int32 nFirstPer, double fRestVal, + double fPer, double fRate, const css::uno::Any& rOB ) +{ + if ( nDate > nFirstPer || fRate <= 0.0 || fRestVal > fCost || + fCost <= 0.0 || fRestVal < 0 || fPer < 0 ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetAmorlinc( GetNullDate( xOpt ), fCost, nDate, nFirstPer, fRestVal, fPer, fRate, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getAccrint( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nIssue, sal_Int32 /*nFirstInter*/, sal_Int32 nSettle, double fRate, + const css::uno::Any &rVal, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fVal = aAnyConv.getDouble( xOpt, rVal, 1000.0 ); + + if( fRate <= 0.0 || fVal <= 0.0 || CHK_Freq || nIssue >= nSettle ) + throw css::lang::IllegalArgumentException(); + + double fRet = fVal * fRate * GetYearDiff( GetNullDate( xOpt ), nIssue, nSettle, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getAccrintm( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nIssue, sal_Int32 nSettle, double fRate, const css::uno::Any& rVal, const css::uno::Any& rOB ) +{ + double fVal = aAnyConv.getDouble( xOpt, rVal, 1000.0 ); + + if( fRate <= 0.0 || fVal <= 0.0 || nIssue >= nSettle ) + throw css::lang::IllegalArgumentException(); + + double fRet = fVal * fRate * GetYearDiff( GetNullDate( xOpt ), nIssue, nSettle, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getReceived( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fInvest, double fDisc, const css::uno::Any& rOB ) +{ + if( fInvest <= 0.0 || fDisc <= 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = fInvest / ( 1.0 - ( fDisc * GetYearDiff( GetNullDate( xOpt ), nSettle, nMat, getDateMode( xOpt, rOB ) ) ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getDisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fPrice, double fRedemp, const css::uno::Any& rOB ) +{ + if( fPrice <= 0.0 || fRedemp <= 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + double fRet = ( 1.0 - fPrice / fRedemp ) / GetYearFrac( xOpt, nSettle, nMat, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getDuration( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fYield, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fCoup < 0.0 || fYield < 0.0 || CHK_Freq || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetDuration( GetNullDate( xOpt ), nSettle, nMat, fCoup, fYield, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getEffect( double fNominal, sal_Int32 nPeriods ) +{ + if( nPeriods < 1 || fNominal <= 0.0 ) + throw css::lang::IllegalArgumentException(); + + double fPeriods = nPeriods; + + double fRet = pow( 1.0 + fNominal / fPeriods, fPeriods ) - 1.0; + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCumprinc( double fRate, sal_Int32 nNumPeriods, double fVal, + sal_Int32 nStartPer, sal_Int32 nEndPer, sal_Int32 nPayType ) +{ + double fPmt, fPpmt; + + if( nStartPer < 1 || nEndPer < nStartPer || fRate <= 0.0 || nEndPer > nNumPeriods || + fVal <= 0.0 || ( nPayType != 0 && nPayType != 1 ) ) + throw css::lang::IllegalArgumentException(); + + fPmt = GetPmt( fRate, nNumPeriods, fVal, 0.0, nPayType ); + + fPpmt = 0.0; + + sal_uInt32 nStart = sal_uInt32( nStartPer ); + sal_uInt32 nEnd = sal_uInt32( nEndPer ); + + if( nStart == 1 ) + { + if( nPayType <= 0 ) + fPpmt = fPmt + fVal * fRate; + else + fPpmt = fPmt; + + nStart++; + } + + for( sal_uInt32 i = nStart ; i <= nEnd ; i++ ) + { + if( nPayType > 0 ) + fPpmt += fPmt - ( GetFv( fRate, double( i - 2 ), fPmt, fVal, 1 ) - fPmt ) * fRate; + else + fPpmt += fPmt - GetFv( fRate, double( i - 1 ), fPmt, fVal, 0 ) * fRate; + } + + RETURN_FINITE( fPpmt ); +} + + +double SAL_CALL AnalysisAddIn::getCumipmt( double fRate, sal_Int32 nNumPeriods, double fVal, + sal_Int32 nStartPer, sal_Int32 nEndPer, sal_Int32 nPayType ) +{ + double fPmt, fIpmt; + + if( nStartPer < 1 || nEndPer < nStartPer || fRate <= 0.0 || nEndPer > nNumPeriods || + fVal <= 0.0 || ( nPayType != 0 && nPayType != 1 ) ) + throw css::lang::IllegalArgumentException(); + + fPmt = GetPmt( fRate, nNumPeriods, fVal, 0.0, nPayType ); + + fIpmt = 0.0; + + sal_uInt32 nStart = sal_uInt32( nStartPer ); + sal_uInt32 nEnd = sal_uInt32( nEndPer ); + + if( nStart == 1 ) + { + if( nPayType <= 0 ) + fIpmt = -fVal; + + nStart++; + } + + for( sal_uInt32 i = nStart ; i <= nEnd ; i++ ) + { + if( nPayType > 0 ) + fIpmt += GetFv( fRate, double( i - 2 ), fPmt, fVal, 1 ) - fPmt; + else + fIpmt += GetFv( fRate, double( i - 1 ), fPmt, fVal, 0 ); + } + + fIpmt *= fRate; + + RETURN_FINITE( fIpmt ); +} + + +double SAL_CALL AnalysisAddIn::getPrice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield, double fRedemp, sal_Int32 nFreq, + const css::uno::Any& rOB ) +{ + if( fYield < 0.0 || fRate < 0.0 || fRedemp <= 0.0 || CHK_Freq || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = getPrice_( GetNullDate( xOpt ), nSettle, nMat, fRate, fYield, fRedemp, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getPricedisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fDisc, double fRedemp, const css::uno::Any& rOB ) +{ + if( fDisc <= 0.0 || fRedemp <= 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = fRedemp * ( 1.0 - fDisc * GetYearDiff( GetNullDate( xOpt ), nSettle, nMat, getDateMode( xOpt, rOB ) ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getPricemat( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, double fRate, double fYield, const css::uno::Any& rOB ) +{ + if( fRate < 0.0 || fYield < 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + sal_Int32 nNullDate = GetNullDate( xOpt ); + sal_Int32 nBase = getDateMode( xOpt, rOB ); + + double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase ); + double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase ); + double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase ); + + double fRet = 1.0 + fIssMat * fRate; + fRet /= 1.0 + fSetMat * fYield; + fRet -= fIssSet * fRate; + fRet *= 100.0; + + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getMduration( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fYield, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fCoup < 0.0 || fYield < 0.0 || CHK_Freq ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetDuration( GetNullDate( xOpt ), nSettle, nMat, fCoup, fYield, nFreq, getDateMode( xOpt, rOB ) ); + fRet /= 1.0 + ( fYield / double( nFreq ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getNominal( double fRate, sal_Int32 nPeriods ) +{ + if( fRate <= 0.0 || nPeriods < 0 ) + throw css::lang::IllegalArgumentException(); + + double fPeriods = nPeriods; + double fRet = ( pow( fRate + 1.0, 1.0 / fPeriods ) - 1.0 ) * fPeriods; + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getDollarfr( double fDollarDec, sal_Int32 nFrac ) +{ + if( nFrac <= 0 ) + throw css::lang::IllegalArgumentException(); + + double fInt; + double fFrac = nFrac; + + double fRet = modf( fDollarDec, &fInt ); + + fRet *= fFrac; + + fRet *= pow( 10.0, -ceil( log10( fFrac ) ) ); + + fRet += fInt; + + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getDollarde( double fDollarFrac, sal_Int32 nFrac ) +{ + if( nFrac <= 0 ) + throw css::lang::IllegalArgumentException(); + + double fInt; + double fFrac = nFrac; + + double fRet = modf( fDollarFrac, &fInt ); + + fRet /= fFrac; + + fRet *= pow( 10.0, ceil( log10( fFrac ) ) ); + + fRet += fInt; + + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getYield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fCoup < 0.0 || fPrice <= 0.0 || fRedemp <= 0.0 || CHK_Freq || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = getYield_( GetNullDate( xOpt ), nSettle, nMat, fCoup, fPrice, fRedemp, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getYielddisc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fPrice, double fRedemp, const css::uno::Any& rOB ) +{ + if( fPrice <= 0.0 || fRedemp <= 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + sal_Int32 nNullDate = GetNullDate( xOpt ); + + double fRet = ( fRedemp / fPrice ) - 1.0; + fRet /= GetYearFrac( nNullDate, nSettle, nMat, getDateMode( xOpt, rOB ) ); + + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getYieldmat( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, double fRate, double fPrice, const css::uno::Any& rOB ) +{ + if( fPrice <= 0.0 || fRate < 0.0 || nSettle >= nMat || nSettle < nIssue) + throw css::lang::IllegalArgumentException(); + + double fRet = GetYieldmat( GetNullDate( xOpt ), nSettle, nMat, nIssue, fRate, fPrice, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getTbilleq( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fDisc ) +{ + nMat++; + + sal_Int32 nDiff = GetDiffDate360( xOpt, nSettle, nMat, true ); + + if( fDisc <= 0.0 || nSettle >= nMat || nDiff > 360 ) + throw css::lang::IllegalArgumentException(); + + double fRet = ( 365 * fDisc ) / ( 360 - ( fDisc * double( nDiff ) ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getTbillprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fDisc ) +{ + if( fDisc <= 0.0 || nSettle > nMat ) + throw css::lang::IllegalArgumentException(); + + nMat++; + + double fFraction = GetYearFrac( xOpt, nSettle, nMat, 0 ); // method: USA 30/360 + + if( modf( fFraction, &o3tl::temporary(double()) ) == 0.0 ) + throw css::lang::IllegalArgumentException(); + + double fRet = 100.0 * ( 1.0 - fDisc * fFraction ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getTbillyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, sal_Int32 nSettle, sal_Int32 nMat, double fPrice ) +{ + sal_Int32 nDiff = GetDiffDate360( xOpt, nSettle, nMat, true ); + nDiff++; + + if( fPrice <= 0.0 || nSettle >= nMat || nDiff > 360 ) + throw css::lang::IllegalArgumentException(); + + double fRet = 100.0; + fRet /= fPrice; + fRet--; + fRet /= double( nDiff ); + fRet *= 360.0; + + RETURN_FINITE( fRet ); +} + +// Encapsulation violation: We *know* that GetOddfprice() always +// throws. + +SAL_WNOUNREACHABLE_CODE_PUSH + +double SAL_CALL AnalysisAddIn::getOddfprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, sal_Int32 nFirstCoup, + double fRate, double fYield, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fRate < 0.0 || fYield < 0.0 || CHK_Freq || nMat <= nFirstCoup || nFirstCoup <= nSettle || nSettle <= nIssue ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetOddfprice( GetNullDate( xOpt ), nSettle, nMat, nIssue, nFirstCoup, fRate, fYield, fRedemp, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + +SAL_WNOUNREACHABLE_CODE_POP + +// Encapsulation violation: We *know* that Getoddfyield() always +// throws. + +SAL_WNOUNREACHABLE_CODE_PUSH + +double SAL_CALL AnalysisAddIn::getOddfyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue, sal_Int32 nFirstCoup, + double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fRate < 0.0 || fPrice <= 0.0 || CHK_Freq || nMat <= nFirstCoup || nFirstCoup <= nSettle || nSettle <= nIssue ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetOddfyield( GetNullDate( xOpt ), nSettle, nMat, nIssue, nFirstCoup, fRate, fPrice, fRedemp, nFreq, + getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + +SAL_WNOUNREACHABLE_CODE_POP + +double SAL_CALL AnalysisAddIn::getOddlprice( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, + double fRate, double fYield, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fRate <= 0.0 || fYield < 0.0 || fRedemp <= 0.0 || CHK_Freq || nMat <= nSettle || nSettle <= nLastInterest ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetOddlprice( GetNullDate( xOpt ), nSettle, nMat, nLastInterest, fRate, fYield, fRedemp, nFreq, + getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getOddlyield( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastInterest, + double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + if( fRate <= 0.0 || fPrice <= 0.0 || fRedemp <= 0.0 || CHK_Freq || nMat <= nSettle || nSettle <= nLastInterest ) + throw css::lang::IllegalArgumentException(); + + double fRet = GetOddlyield( GetNullDate( xOpt ), nSettle, nMat, nLastInterest, fRate, fPrice, fRedemp, nFreq, + getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + +// XIRR helper functions + +#define V_(i) (rValues.Get(i)) +#define D_(i) (rDates.Get(i)) + +/** Calculates the resulting amount for the passed interest rate and the given XIRR parameters. */ +static double lcl_sca_XirrResult( const ScaDoubleList& rValues, const ScaDoubleList& rDates, double fRate ) +{ + /* V_0 ... V_n = input values. + D_0 ... D_n = input dates. + R = input interest rate. + + r := R+1 + E_i := (D_i-D_0) / 365 + + n V_i n V_i + f(R) = SUM ------- = V_0 + SUM ------- . + i=0 r^E_i i=1 r^E_i + */ + double D_0 = D_(0); + double r = fRate + 1.0; + double fResult = V_(0); + for( sal_uInt32 i = 1, nCount = rValues.Count(); i < nCount; ++i ) + fResult += V_(i) / pow( r, (D_(i) - D_0) / 365.0 ); + return fResult; +} + +/** Calculates the first derivation of lcl_sca_XirrResult(). */ +static double lcl_sca_XirrResult_Deriv1( const ScaDoubleList& rValues, const ScaDoubleList& rDates, double fRate ) +{ + /* V_0 ... V_n = input values. + D_0 ... D_n = input dates. + R = input interest rate. + + r := R+1 + E_i := (D_i-D_0) / 365 + + n V_i + f'(R) = [ V_0 + SUM ------- ]' + i=1 r^E_i + + n V_i n E_i V_i + = 0 + SUM -E_i ----------- r' = - SUM ----------- . + i=1 r^(E_i+1) i=1 r^(E_i+1) + */ + double D_0 = D_(0); + double r = fRate + 1.0; + double fResult = 0.0; + for( sal_uInt32 i = 1, nCount = rValues.Count(); i < nCount; ++i ) + { + double E_i = (D_(i) - D_0) / 365.0; + fResult -= E_i * V_(i) / pow( r, E_i + 1.0 ); + } + return fResult; +} + +#undef V_ +#undef D_ + +// XIRR calculation + +double SAL_CALL AnalysisAddIn::getXirr( + const css::uno::Reference< css::beans::XPropertySet >& xOpt, const css::uno::Sequence< css::uno::Sequence< double > >& rValues, const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rDates, const css::uno::Any& rGuessRate ) +{ + ScaDoubleList aValues, aDates; + aValues.Append( rValues ); + aDates.Append( rDates ); + + if( (aValues.Count() < 2) || (aValues.Count() != aDates.Count()) ) + throw css::lang::IllegalArgumentException(); + + // result interest rate, initialized with passed guessed rate, or 10% + double fResultRate = aAnyConv.getDouble( xOpt, rGuessRate, 0.1 ); + if( fResultRate <= -1 ) + throw css::lang::IllegalArgumentException(); + + // maximum epsilon for end of iteration + static const double fMaxEps = 1e-10; + // maximum number of iterations + static const sal_Int32 nMaxIter = 50; + + // Newton's method - try to find a fResultRate, so that lcl_sca_XirrResult() returns 0. + sal_Int32 nIter = 0; + double fResultValue; + sal_Int32 nIterScan = 0; + bool bContLoop = false; + bool bResultRateScanEnd = false; + + // First the inner while-loop will be executed using the default Value fResultRate + // or the user guessed fResultRate if those do not deliver a solution for the + // Newton's method then the range from -0.99 to +0.99 will be scanned with a + // step size of 0.01 to find fResultRate's value which can deliver a solution + do + { + if (nIterScan >=1) + fResultRate = -0.99 + (nIterScan -1)* 0.01; + do + { + fResultValue = lcl_sca_XirrResult( aValues, aDates, fResultRate ); + double fNewRate = fResultRate - fResultValue / lcl_sca_XirrResult_Deriv1( aValues, aDates, fResultRate ); + double fRateEps = fabs( fNewRate - fResultRate ); + fResultRate = fNewRate; + bContLoop = (fRateEps > fMaxEps) && (fabs( fResultValue ) > fMaxEps); + } + while( bContLoop && (++nIter < nMaxIter) ); + nIter = 0; + if ( std::isnan(fResultRate) || std::isinf(fResultRate) + ||std::isnan(fResultValue) || std::isinf(fResultValue)) + bContLoop = true; + + ++nIterScan; + bResultRateScanEnd = (nIterScan >= 200); + } + while(bContLoop && !bResultRateScanEnd); + + if( bContLoop ) + throw css::lang::IllegalArgumentException(); + RETURN_FINITE( fResultRate ); +} + + +double SAL_CALL AnalysisAddIn::getXnpv( + double fRate, const css::uno::Sequence< css::uno::Sequence< double > >& rValues, const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rDates ) +{ + ScaDoubleList aValList; + ScaDoubleList aDateList; + + aValList.Append( rValues ); + aDateList.Append( rDates ); + + sal_Int32 nNum = aValList.Count(); + + if( nNum != sal_Int32( aDateList.Count() ) || nNum < 2 ) + throw css::lang::IllegalArgumentException(); + + double fRet = 0.0; + double fNull = aDateList.Get( 0 ); + fRate++; + + for( sal_Int32 i = 0 ; i < nNum ; i++ ) + fRet += aValList.Get( i ) / ( pow( fRate, ( aDateList.Get( i ) - fNull ) / 365.0 ) ); + + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getIntrate( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, double fInvest, double fRedemp, const css::uno::Any& rOB ) +{ + if( fInvest <= 0.0 || fRedemp <= 0.0 || nSettle >= nMat ) + throw css::lang::IllegalArgumentException(); + + double fRet = ( ( fRedemp / fInvest ) - 1.0 ) / GetYearDiff( GetNullDate( xOpt ), nSettle, nMat, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCoupncd( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCoupncd( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCoupdays( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCoupdays( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCoupdaysnc( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCoupdaysnc( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCoupdaybs( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCoupdaybs( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCouppcd( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCouppcd( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getCoupnum( const css::uno::Reference< css::beans::XPropertySet >& xOpt, + sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, const css::uno::Any& rOB ) +{ + double fRet = GetCoupnum( GetNullDate( xOpt ), nSettle, nMat, nFreq, getDateMode( xOpt, rOB ) ); + RETURN_FINITE( fRet ); +} + + +double SAL_CALL AnalysisAddIn::getFvschedule( double fPrinc, const css::uno::Sequence< css::uno::Sequence< double > >& rSchedule ) +{ + ScaDoubleList aSchedList; + + aSchedList.Append( rSchedule ); + + for( sal_uInt32 i = 0; i < aSchedList.Count(); ++i ) + fPrinc *= 1.0 + aSchedList.Get(i); + + RETURN_FINITE( fPrinc ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/datefunc/date.component b/scaddins/source/datefunc/date.component new file mode 100644 index 000000000..7bbf766a5 --- /dev/null +++ b/scaddins/source/datefunc/date.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="date" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sheet.addin.DateFunctionsImpl"> + <service name="com.sun.star.sheet.AddIn"/> + <service name="com.sun.star.sheet.addin.DateFunctions"/> + </implementation> +</component> diff --git a/scaddins/source/datefunc/datefunc.cxx b/scaddins/source/datefunc/datefunc.cxx new file mode 100644 index 000000000..f47b9b74a --- /dev/null +++ b/scaddins/source/datefunc/datefunc.cxx @@ -0,0 +1,760 @@ +/* -*- 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 "datefunc.hxx" +#include <datefunc.hrc> +#include <strings.hrc> +#include <com/sun/star/util/Date.hpp> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <rtl/ustrbuf.hxx> +#include <unotools/resmgr.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <algorithm> +#include "deffuncname.hxx" + +using namespace ::com::sun::star; + +#define ADDIN_SERVICE "com.sun.star.sheet.AddIn" +#define MY_SERVICE "com.sun.star.sheet.addin.DateFunctions" +#define MY_IMPLNAME "com.sun.star.sheet.addin.DateFunctionsImpl" + +#define UNIQUE false // function name does not exist in Calc + +#define STDPAR false // all parameters are described +#define INTPAR true // first parameter is internal + +#define FUNCDATA( FuncName, ParamCount, Category, Double, IntPar ) \ + { "get" #FuncName, DATE_FUNCNAME_##FuncName, DATE_FUNCDESC_##FuncName, DATE_DEFFUNCNAME_##FuncName, ParamCount, Category, Double, IntPar } + +const ScaFuncDataBase pFuncDataArr[] = +{ + FUNCDATA( DiffWeeks, 3, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( DiffMonths, 3, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( DiffYears, 3, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( IsLeapYear, 1, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( DaysInMonth, 1, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( DaysInYear, 1, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( WeeksInYear, 1, ScaCategory::DateTime, UNIQUE, INTPAR ), + FUNCDATA( Rot13, 1, ScaCategory::Text, UNIQUE, STDPAR ) +}; + +#undef FUNCDATA + +ScaFuncData::ScaFuncData(const ScaFuncDataBase& rBaseData) : + aIntName( OUString::createFromAscii( rBaseData.pIntName ) ), + pUINameID( rBaseData.pUINameID ), + pDescrID( rBaseData.pDescrID ), + nParamCount( rBaseData.nParamCount ), + eCat( rBaseData.eCat ), + bDouble( rBaseData.bDouble ), + bWithOpt( rBaseData.bWithOpt ) +{ + aCompList.push_back(OUString::createFromAscii(rBaseData.pCompListID[0])); + aCompList.push_back(OUString::createFromAscii(rBaseData.pCompListID[1])); +} + +sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const +{ + if( !bWithOpt ) + nParam++; + return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2); +} + +static void InitScaFuncDataList(ScaFuncDataList& rList) +{ + for (const auto & nIndex : pFuncDataArr) + rList.push_back(ScaFuncData(nIndex)); +} + +// entry points for service registration / instantiation +static uno::Reference< uno::XInterface > ScaDateAddIn_CreateInstance( + const uno::Reference< lang::XMultiServiceFactory >& ) +{ + return static_cast<cppu::OWeakObject*>(new ScaDateAddIn()); +} + +extern "C" { + +SAL_DLLPUBLIC_EXPORT void * date_component_getFactory( + const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void* pRet = nullptr; + + if ( pServiceManager && + OUString::createFromAscii( pImplName ) == ScaDateAddIn::getImplementationName_Static() ) + { + uno::Reference< lang::XSingleServiceFactory > xFactory( cppu::createOneInstanceFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + ScaDateAddIn::getImplementationName_Static(), + ScaDateAddIn_CreateInstance, + ScaDateAddIn::getSupportedServiceNames_Static() ) ); + + if (xFactory.is()) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +} // extern C + +// "normal" service implementation +ScaDateAddIn::ScaDateAddIn() +{ +} + +static const char* pLang[] = { "de", "en" }; +static const char* pCoun[] = { "DE", "US" }; +static const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS( pLang ); + +void ScaDateAddIn::InitDefLocales() +{ + pDefLocales.reset(new lang::Locale[ nNumOfLoc ]); + + for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ ) + { + pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] ); + pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] ); + } +} + +const lang::Locale& ScaDateAddIn::GetLocale( sal_uInt32 nIndex ) +{ + if( !pDefLocales ) + InitDefLocales(); + + return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc; +} + +void ScaDateAddIn::InitData() +{ + aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc)); + pFuncDataList.reset(); + + pFuncDataList.reset(new ScaFuncDataList); + InitScaFuncDataList(*pFuncDataList); + + if( pDefLocales ) + { + pDefLocales.reset(); + } +} + +OUString ScaDateAddIn::GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex) +{ + return ScaResId(pResId[nStrIndex - 1]); +} + +OUString ScaDateAddIn::getImplementationName_Static() +{ + return MY_IMPLNAME; +} + +uno::Sequence< OUString > ScaDateAddIn::getSupportedServiceNames_Static() +{ + return { ADDIN_SERVICE, MY_SERVICE }; +} + +// XServiceName +OUString SAL_CALL ScaDateAddIn::getServiceName() +{ + // name of specific AddIn service + return MY_SERVICE; +} + +// XServiceInfo +OUString SAL_CALL ScaDateAddIn::getImplementationName() +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL ScaDateAddIn::supportsService( const OUString& aServiceName ) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence< OUString > SAL_CALL ScaDateAddIn::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +// XLocalizable +void SAL_CALL ScaDateAddIn::setLocale( const lang::Locale& eLocale ) +{ + aFuncLoc = eLocale; + InitData(); // change of locale invalidates resources! +} + +lang::Locale SAL_CALL ScaDateAddIn::getLocale() +{ + return aFuncLoc; +} + +OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& ) +{ + // not used by calc + // (but should be implemented for other uses of the AddIn service) + return OUString(); +} + +OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() ) + { + aRet = ScaResId(fDataIt->GetUINameID()); + if( fDataIt->IsDouble() ) + aRet += "_ADD"; + } + else + { + aRet = "UNKNOWNFUNC_" + aProgrammaticName; + } + + return aRet; +} + +OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), 1 ); + + return aRet; +} + +OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName( + const OUString& aProgrammaticName, sal_Int32 nArgument ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) ) + { + sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); + if( nStr ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr ); + else + aRet = "internal"; + } + + return aRet; +} + +OUString SAL_CALL ScaDateAddIn::getArgumentDescription( + const OUString& aProgrammaticName, sal_Int32 nArgument ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) ) + { + sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); + if( nStr ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr + 1 ); + else + aRet = "for internal use only"; + } + + return aRet; +} + +OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName( + const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() ) + { + switch( fDataIt->GetCategory() ) + { + case ScaCategory::DateTime: aRet = "Date&Time"; break; + case ScaCategory::Text: aRet = "Text"; break; + case ScaCategory::Finance: aRet = "Financial"; break; + case ScaCategory::Inf: aRet = "Information"; break; + case ScaCategory::Math: aRet = "Mathematical"; break; + case ScaCategory::Tech: aRet = "Technical"; break; + } + } + + if( aRet.isEmpty() ) + aRet = "Add-In"; + return aRet; +} + +OUString SAL_CALL ScaDateAddIn::getDisplayCategoryName( + const OUString& aProgrammaticName ) +{ + return getProgrammaticCategoryName( aProgrammaticName ); +} + +// XCompatibilityNames +uno::Sequence< sheet::LocalizedName > SAL_CALL ScaDateAddIn::getCompatibilityNames( + const OUString& aProgrammaticName ) +{ + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt == pFuncDataList->end() ) + return uno::Sequence< sheet::LocalizedName >( 0 ); + + const std::vector<OUString>& rStrList = fDataIt->GetCompNameList(); + sal_uInt32 nCount = rStrList.size(); + + uno::Sequence< sheet::LocalizedName > aRet( nCount ); + sheet::LocalizedName* pArray = aRet.getArray(); + + for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ ) + pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), rStrList.at( nIndex ) ); + + return aRet; +} + +namespace { + +// auxiliary functions +bool IsLeapYear( sal_uInt16 nYear ) +{ + return ((((nYear % 4) == 0) && ((nYear % 100) != 0)) || ((nYear % 400) == 0)); +} + +sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear ) +{ + static const sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; + + if ( nMonth != 2 ) + return aDaysInMonth[nMonth-1]; + else + { + if ( IsLeapYear(nYear) ) + return aDaysInMonth[nMonth-1] + 1; + else + return aDaysInMonth[nMonth-1]; + } +} + +/** + * 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. + * + * @throws lang::IllegalArgumentException + */ + +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 + * + * @throws uno::RuntimeException + */ +sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOptions ) +{ + if (xOptions.is()) + { + try + { + uno::Any aAny = xOptions->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(); +} + +} +// XDateFunctions + +/** + * Get week difference between 2 dates + * + * new Weeks(date1,date2,mode) function for StarCalc + * + * Two modes of operation are provided. + * The first is just a simple division by 7 calculation. + * + * The second calculates the difference by week of year. + * + * The International Standard IS-8601 has decreed that Monday + * shall be the first day of the week. + * + * A week that lies partly in one year and partly in another + * is assigned a number in the year in which most of its days lie. + * + * That means that week 1 of any year is the week that contains the 4. January + * + * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001 + * + * A WeekDay can be then calculated by subtracting 1 and calculating the rest of + * a division by 7, which gives a 0 - 6 value for Monday - Sunday + * + * Using the 4. January rule explained above the formula + * + * nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1; + * + * calculates a number between 0-53 for each day which is in the same year as nJan4 + * where 0 means that this week belonged to the year before. + * + * If a day in the same or another year is used in this formula this calculates + * a calendar week offset from a given 4. January + * + * nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1; + * + * The 4.January of first Date Argument can thus be used to calculate + * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1 + * + * which can be optimized to + * + * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ) + * + * Note: All calculations are operating on the long integer data type + * % is the modulo operator in C which calculates the rest of an Integer division + * + * + * mode 0 is the interval between the dates in month, that is days / 7 + * + * mode 1 is the difference by week of year + * + */ + +sal_Int32 SAL_CALL ScaDateAddIn::getDiffWeeks( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nStartDate, sal_Int32 nEndDate, + sal_Int32 nMode ) +{ + if (nMode != 0 && nMode != 1) + throw lang::IllegalArgumentException(); + + sal_Int32 nNullDate = GetNullDate( xOptions ); + + sal_Int32 nDays1 = nStartDate + nNullDate; + sal_Int32 nDays2 = nEndDate + nNullDate; + + sal_Int32 nRet; + + if ( nMode == 1 ) + { + sal_uInt16 nDay,nMonth,nYear; + DaysToDate( nDays1, nDay, nMonth, nYear ); + sal_Int32 nJan4 = DateToDays( 4, 1, nYear ); + + nRet = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ); + } + else + { + nRet = (nDays2 - nDays1) / 7; + } + return nRet; +} + +/** + * Get month difference between 2 dates + * =Month(start, end, mode) Function for StarCalc + * + * two modes are provided + * + * mode 0 is the interval between the dates in month + * + * mode 1 is the difference in calendar month + */ +sal_Int32 SAL_CALL ScaDateAddIn::getDiffMonths( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nStartDate, sal_Int32 nEndDate, + sal_Int32 nMode ) +{ + if (nMode != 0 && nMode != 1) + throw lang::IllegalArgumentException(); + + sal_Int32 nNullDate = GetNullDate( xOptions ); + + sal_Int32 nDays1 = nStartDate + nNullDate; + sal_Int32 nDays2 = nEndDate + nNullDate; + + sal_uInt16 nDay1,nMonth1,nYear1; + sal_uInt16 nDay2,nMonth2,nYear2; + DaysToDate(nDays1,nDay1,nMonth1,nYear1); + DaysToDate(nDays2,nDay2,nMonth2,nYear2); + + sal_Int32 nRet = nMonth2 - nMonth1 + (nYear2 - nYear1) * 12; + if ( nMode == 1 || nDays1 == nDays2 ) return nRet; + + if ( nDays1 < nDays2 ) + { + if ( nDay1 > nDay2 ) + { + nRet -= 1; + } + } + else + { + if ( nDay1 < nDay2 ) + { + nRet += 1; + } + } + + return nRet; +} + +/** + * Get Year difference between 2 dates + * + * two modes are provided + * + * mode 0 is the interval between the dates in years + * + * mode 1 is the difference in calendar years + */ +sal_Int32 SAL_CALL ScaDateAddIn::getDiffYears( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nStartDate, sal_Int32 nEndDate, + sal_Int32 nMode ) +{ + if (nMode != 0 && nMode != 1) + throw lang::IllegalArgumentException(); + + if ( nMode != 1 ) + return getDiffMonths( xOptions, nStartDate, nEndDate, nMode ) / 12; + + sal_Int32 nNullDate = GetNullDate( xOptions ); + + sal_Int32 nDays1 = nStartDate + nNullDate; + sal_Int32 nDays2 = nEndDate + nNullDate; + + sal_uInt16 nDay1,nMonth1,nYear1; + sal_uInt16 nDay2,nMonth2,nYear2; + DaysToDate(nDays1,nDay1,nMonth1,nYear1); + DaysToDate(nDays2,nDay2,nMonth2,nYear2); + + return nYear2 - nYear1; +} + +/** + * Check if a Date is in a leap year in the Gregorian calendar + */ +sal_Int32 SAL_CALL ScaDateAddIn::getIsLeapYear( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nDate ) +{ + sal_Int32 nNullDate = GetNullDate( xOptions ); + sal_Int32 nDays = nDate + nNullDate; + + sal_uInt16 nDay, nMonth, nYear; + DaysToDate(nDays,nDay,nMonth,nYear); + + return static_cast<sal_Int32>(IsLeapYear(nYear)); +} + +/** + * Get the Number of Days in the month for a date + */ +sal_Int32 SAL_CALL ScaDateAddIn::getDaysInMonth( + const uno::Reference<beans::XPropertySet>& xOptions, + sal_Int32 nDate ) +{ + sal_Int32 nNullDate = GetNullDate( xOptions ); + sal_Int32 nDays = nDate + nNullDate; + + sal_uInt16 nDay, nMonth, nYear; + DaysToDate(nDays,nDay,nMonth,nYear); + + return DaysInMonth( nMonth, nYear ); +} + +/** + * Get number of days in the year of a date specified + */ +sal_Int32 SAL_CALL ScaDateAddIn::getDaysInYear( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nDate ) +{ + sal_Int32 nNullDate = GetNullDate( xOptions ); + sal_Int32 nDays = nDate + nNullDate; + + sal_uInt16 nDay, nMonth, nYear; + DaysToDate(nDays,nDay,nMonth,nYear); + + return ( IsLeapYear(nYear) ? 366 : 365 ); +} + +/** + * Get number of weeks in the year for a date + * + * Most years have 52 weeks, but years that start on a Thursday + * and leap years that start on a Wednesday have 53 weeks + * + * The International Standard IS-8601 has decreed that Monday + * shall be the first day of the week. + * + * A WeekDay can be calculated by subtracting 1 and calculating the rest of + * a division by 7 from the internal date representation + * which gives a 0 - 6 value for Monday - Sunday + * + * @see #IsLeapYear #WeekNumber + */ +sal_Int32 SAL_CALL ScaDateAddIn::getWeeksInYear( + const uno::Reference< beans::XPropertySet >& xOptions, + sal_Int32 nDate ) +{ + sal_Int32 nNullDate = GetNullDate( xOptions ); + sal_Int32 nDays = nDate + nNullDate; + + sal_uInt16 nDay, nMonth, nYear; + DaysToDate(nDays,nDay,nMonth,nYear); + + sal_Int32 nJan1WeekDay = ( DateToDays(1,1,nYear) - 1) % 7; + + sal_Int32 nRet; + if ( nJan1WeekDay == 3 ) /* Thursday */ + nRet = 53; + else if ( nJan1WeekDay == 2 ) /* Wednesday */ + nRet = ( IsLeapYear(nYear) ? 53 : 52 ); + else + nRet = 52; + + return nRet; +} + +/** + * Encrypt or decrypt a string using ROT13 algorithm + * + * This function rotates each character by 13 in the alphabet. + * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified. + */ +OUString SAL_CALL ScaDateAddIn::getRot13( const OUString& aSrcString ) +{ + OUStringBuffer aBuffer( aSrcString ); + for( sal_Int32 nIndex = 0; nIndex < aBuffer.getLength(); nIndex++ ) + { + sal_Unicode cChar = aBuffer[nIndex]; + if( (cChar >= 'a') && (cChar <= 'z')) + { + cChar += 13; + if (cChar > 'z') + cChar -= 26; + } + else if( (cChar >= 'A') && (cChar <= 'Z') ) + { + cChar += 13; + if (cChar > 'Z') + cChar -= 26; + } + aBuffer[nIndex] = cChar; + } + return aBuffer.makeStringAndClear(); +} + +OUString ScaDateAddIn::ScaResId(const char* pId) +{ + return Translate::get(pId, aResLocale); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/datefunc/datefunc.hxx b/scaddins/source/datefunc/datefunc.hxx new file mode 100644 index 000000000..528a6d2f0 --- /dev/null +++ b/scaddins/source/datefunc/datefunc.hxx @@ -0,0 +1,198 @@ +/* -*- 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 . + */ + +// date functions add in + +#ifndef INCLUDED_SCADDINS_SOURCE_DATEFUNC_DATEFUNC_HXX +#define INCLUDED_SCADDINS_SOURCE_DATEFUNC_DATEFUNC_HXX + +#include <vector> +#include <memory> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sheet/XAddIn.hpp> +#include <com/sun/star/sheet/XCompatibilityNames.hpp> +#include <com/sun/star/sheet/addin/XDateFunctions.hpp> +#include <com/sun/star/sheet/addin/XMiscFunctions.hpp> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::lang { class XMultiServiceFactory; } + +enum class ScaCategory +{ + DateTime, + Text, + Finance, + Inf, + Math, + Tech +}; + +struct ScaFuncDataBase +{ + const char* pIntName; // internal name (get***) + const char* pUINameID; // resource ID to UI name + const char** pDescrID; // resource ID to description, parameter names and ~ description + const char** pCompListID; // list of valid names + sal_uInt16 nParamCount; // number of named / described parameters + ScaCategory eCat; // function category + bool bDouble; // name already exist in Calc + bool bWithOpt; // first parameter is internal +}; + +class ScaFuncData final +{ +private: + OUString aIntName; // internal name (get***) + const char* pUINameID; // resource ID to UI name + const char** pDescrID; // leads also to parameter descriptions! + sal_uInt16 nParamCount; // num of parameters + std::vector<OUString> aCompList; // list of all valid names + ScaCategory eCat; // function category + bool bDouble; // name already exist in Calc + bool bWithOpt; // first parameter is internal + +public: + ScaFuncData(const ScaFuncDataBase& rBaseData); + + const char* GetUINameID() const { return pUINameID; } + const char** GetDescrID() const { return pDescrID; } + ScaCategory GetCategory() const { return eCat; } + bool IsDouble() const { return bDouble; } + + sal_uInt16 GetStrIndex( sal_uInt16 nParam ) const; + bool Is( const OUString& rCompare ) const + { return aIntName == rCompare; } + + const std::vector<OUString>& GetCompNameList() const { return aCompList; } +}; + +typedef std::vector<ScaFuncData> ScaFuncDataList; + +// Predicate for use with std::find_if +struct FindScaFuncData +{ + const OUString& m_rId; + explicit FindScaFuncData( const OUString& rId ) : m_rId(rId) {} + bool operator() ( ScaFuncData const & rCandidate ) const { return rCandidate.Is(m_rId); } +}; + + +css::uno::Reference< css::uno::XInterface > SAL_CALL DateFunctionAddIn_CreateInstance( + const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + + +// THE AddIn class for date functions + +class ScaDateAddIn : public ::cppu::WeakImplHelper< + css::sheet::XAddIn, + css::sheet::XCompatibilityNames, + css::sheet::addin::XDateFunctions, + css::sheet::addin::XMiscFunctions, + css::lang::XServiceName, + css::lang::XServiceInfo > +{ +private: + css::lang::Locale aFuncLoc; + std::unique_ptr< css::lang::Locale[] > pDefLocales; + std::locale aResLocale; + std::unique_ptr< ScaFuncDataList > pFuncDataList; + + + void InitDefLocales(); + const css::lang::Locale& GetLocale( sal_uInt32 nIndex ); + void InitData(); + + /// @throws css::uno::RuntimeException + OUString GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex); + +public: + ScaDateAddIn(); + + OUString ScaResId(const char* pId); + + static OUString getImplementationName_Static(); + static css::uno::Sequence< OUString > getSupportedServiceNames_Static(); + + // XAddIn + virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) override; + virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getDisplayArgumentName( const OUString& aProgrammaticName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getArgumentDescription( const OUString& aProgrammaticName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getProgrammaticCategoryName( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getDisplayCategoryName( const OUString& aProgrammaticName ) override; + + // XCompatibilityNames + virtual css::uno::Sequence< css::sheet::LocalizedName > SAL_CALL getCompatibilityNames( const OUString& aProgrammaticName ) override; + + // XLocalizable + virtual void SAL_CALL setLocale( const css::lang::Locale& eLocale ) override; + virtual css::lang::Locale SAL_CALL getLocale() override; + + // XServiceName + virtual OUString SAL_CALL getServiceName() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // methods from own interfaces start here + + // XDateFunctions + virtual sal_Int32 SAL_CALL getDiffWeeks( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nEndDate, sal_Int32 nStartDate, + sal_Int32 nMode ) override; + + virtual sal_Int32 SAL_CALL getDiffMonths( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nEndDate, sal_Int32 nStartDate, + sal_Int32 nMode ) override; + + virtual sal_Int32 SAL_CALL getDiffYears( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nEndDate, sal_Int32 nStartDate, + sal_Int32 nMode ) override; + + virtual sal_Int32 SAL_CALL getIsLeapYear( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nDate ) override; + + virtual sal_Int32 SAL_CALL getDaysInMonth( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nDate ) override; + + virtual sal_Int32 SAL_CALL getDaysInYear( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nDate ) override; + + virtual sal_Int32 SAL_CALL getWeeksInYear( + const css::uno::Reference< css::beans::XPropertySet >& xOptions, + sal_Int32 nDate ) override; + + // XMiscFunctions + virtual OUString SAL_CALL getRot13( + const OUString& aSrcText ) override; +}; + +#endif // INCLUDED_SCADDINS_SOURCE_DATEFUNC_DATEFUNC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/datefunc/deffuncname.hxx b/scaddins/source/datefunc/deffuncname.hxx new file mode 100644 index 000000000..d1d807e3d --- /dev/null +++ b/scaddins/source/datefunc/deffuncname.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SCADDINS_SOURCE_DATEFUNC_DEFFUNCTIONNAME_HXX +#define INCLUDED_SCADDINS_SOURCE_DATEFUNC_DEFFUNCTIONNAME_HXX + +static const char* DATE_DEFFUNCNAME_DiffWeeks[2] = +{ + "WOCHEN", + "WEEKS" +}; + +static const char* DATE_DEFFUNCNAME_DiffMonths[2] = +{ + "MONATE", + "MONTHS" +}; + +static const char* DATE_DEFFUNCNAME_DiffYears[2] = +{ + "JAHRE", + "YEARS" +}; + +static const char* DATE_DEFFUNCNAME_IsLeapYear[2] = +{ + "ISTSCHALTJAHR", + "ISLEAPYEAR" +}; + +static const char* DATE_DEFFUNCNAME_DaysInMonth[2] = +{ + "TAGEIMMONAT", + "DAYSINMONTH" +}; + +static const char* DATE_DEFFUNCNAME_DaysInYear[2] = +{ + "TAGEIMJAHR", + "DAYSINYEAR" +}; + +static const char* DATE_DEFFUNCNAME_WeeksInYear[2] = +{ + "WOCHENIMJAHR", + "WEEKSINYEAR" +}; + +static const char* DATE_DEFFUNCNAME_Rot13[2] = +{ + "ROT13", + "ROT13" +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/pricing/black_scholes.cxx b/scaddins/source/pricing/black_scholes.cxx new file mode 100644 index 000000000..57344d85c --- /dev/null +++ b/scaddins/source/pricing/black_scholes.cxx @@ -0,0 +1,940 @@ +/* -*- 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/. + * + * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de> + * + */ + +#include <cstdio> +#include <cstdlib> +#include <cmath> +#include <cassert> +#include <algorithm> +#include <rtl/math.hxx> +#include "black_scholes.hxx" + +// options prices and greeks in the Black-Scholes model +// also known as TV (theoretical value) + +// the code is structured as follows: + +// (1) basic assets +// - cash-or-nothing option: bincash() +// - asset-or-nothing option: binasset() + +// (2) derived basic assets, can all be priced based on (1) +// - vanilla put/call: putcall() = +/- ( binasset() - K*bincash() ) +// - truncated put/call (barriers active at maturity only) + +// (3) write a wrapper function to include all vanilla prices +// - this is so we don't duplicate code when pricing barriers +// as this is derived from vanillas + +// (4) single barrier options (knock-out), priced based on truncated vanillas +// - it follows from the reflection principle that the price W(S) of a +// single barrier option is given by +// W(S) = V(S) - (B/S)^a V(B^2/S), a = 2(rd-rf)/vol^2 - 1 +// where V(S) is the price of the corresponding truncated vanilla +// option +// - to reduce code duplication and in anticipation of double barrier +// options we write the following function +// barrier_term(S,c) = V(c*S) - (B/S)^a V(c*B^2/S) + +// (5) double barrier options (knock-out) +// - value is an infinite sum over option prices of the corresponding +// truncated vanillas (truncated at both barriers): + +// W(S)=sum (B2/B1)^(i*a) (V(S(B2/B1)^(2i)) - (B1/S)^a V(B1^2/S (B2/B1)^(2i)) + +// (6) write routines for put/call barriers and touch options which +// mainly call the general double barrier pricer +// the main routines are touch() and barrier() +// both can price in/out barriers, double/single barriers as well as +// vanillas + + +// the framework allows any barriers to be priced as long as we define +// the value/greek functions for the corresponding truncated vanilla +// and wrap them into internal::vanilla() and internal::vanilla_trunc() + +// disadvantage of that approach is that due to the rules of +// differentiations the formulas for greeks become long and possible +// simplifications in the formulas won't be made + +// other code inefficiency due to multiplication with pm (+/- 1) +// cvtsi2sd: int-->double, 6/3 cycles +// mulsd: double-double multiplication, 5/1 cycles +// with -O3, however, it compiles 2 versions with pm=1, and pm=-1 +// which are efficient +// note this is tiny anyway as compared to exp/log (100 cycles), +// pow (200 cycles), erf (70 cycles) + +// this code is not tested for numerical instability, ie overruns, +// underruns, accuracy, etc + + +namespace sca::pricing::bs { + + +// helper functions + +static double sqr(double x) { + return x*x; +} +// normal density (see also ScInterpreter::phi) +static double dnorm(double x) { + //return (1.0/sqrt(2.0*M_PI))*exp(-0.5*x*x); // windows may not have M_PI + return 0.39894228040143268*exp(-0.5*x*x); +} +// cumulative normal distribution (see also ScInterpreter::integralPhi) +static double pnorm(double x) { + //return 0.5*(erf(sqrt(0.5)*x)+1.0); // windows may not have erf + return 0.5 * ::rtl::math::erfc(-x * 0.7071067811865475); +} + +// binary option cash (domestic) +// call - pays 1 if S_T is above strike K +// put - pays 1 if S_T is below strike K +double bincash(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall pc, types::Greeks greeks) { + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + + double val=0.0; + + if(tau<=0.0) { + // special case tau=0 (expiry) + switch(greeks) { + case types::Value: + if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { + val = 1.0; + } else { + val = 0.0; + } + break; + default: + val = 0.0; + } + } else if(K==0.0) { + // special case with zero strike + if(pc==types::Put) { + // up-and-out (put) with K=0 + val=0.0; + } else { + // down-and-out (call) with K=0 (zero coupon bond) + switch(greeks) { + case types::Value: + val = 1.0; + break; + case types::Theta: + val = rd; + break; + case types::Rho_d: + val = -tau; + break; + default: + val = 0.0; + } + } + } else { + // standard case with K>0, tau>0 + double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); + double d2 = d1 - vol*sqrt(tau); + int pm = (pc==types::Call) ? 1 : -1; + + switch(greeks) { + case types::Value: + val = pnorm(pm*d2); + break; + case types::Delta: + val = pm*dnorm(d2)/(S*vol*sqrt(tau)); + break; + case types::Gamma: + val = -pm*dnorm(d2)*d1/(sqr(S*vol)*tau); + break; + case types::Theta: + val = rd*pnorm(pm*d2) + + pm*dnorm(d2)*(log(S/K)/(vol*sqrt(tau))-0.5*d2)/tau; + break; + case types::Vega: + val = -pm*dnorm(d2)*d1/vol; + break; + case types::Volga: + val = pm*dnorm(d2)/(vol*vol)*(-d1*d1*d2+d1+d2); + break; + case types::Vanna: + val = pm*dnorm(d2)/(S*vol*vol*sqrt(tau))*(d1*d2-1.0); + break; + case types::Rho_d: + val = -tau*pnorm(pm*d2) + pm*dnorm(d2)*sqrt(tau)/vol; + break; + case types::Rho_f: + val = -pm*dnorm(d2)*sqrt(tau)/vol; + break; + default: + printf("bincash: greek %d not implemented\n", greeks ); + abort(); + } + } + return exp(-rd*tau)*val; +} + +// binary option asset (foreign) +// call - pays S_T if S_T is above strike K +// put - pays S_T if S_T is below strike K +double binasset(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall pc, types::Greeks greeks) { + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + + double val=0.0; + if(tau<=0.0) { + // special case tau=0 (expiry) + switch(greeks) { + case types::Value: + if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { + val = S; + } else { + val = 0.0; + } + break; + case types::Delta: + if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { + val = 1.0; + } else { + val = 0.0; + } + break; + default: + val = 0.0; + } + } else if(K==0.0) { + // special case with zero strike (forward with zero strike) + if(pc==types::Put) { + // up-and-out (put) with K=0 + val = 0.0; + } else { + // down-and-out (call) with K=0 (type of forward) + switch(greeks) { + case types::Value: + val = S; + break; + case types::Delta: + val = 1.0; + break; + case types::Theta: + val = rf*S; + break; + case types::Rho_f: + val = -tau*S; + break; + default: + val = 0.0; + } + } + } else { + // normal case + double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); + double d2 = d1 - vol*sqrt(tau); + int pm = (pc==types::Call) ? 1 : -1; + + switch(greeks) { + case types::Value: + val = S*pnorm(pm*d1); + break; + case types::Delta: + val = pnorm(pm*d1) + pm*dnorm(d1)/(vol*sqrt(tau)); + break; + case types::Gamma: + val = -pm*dnorm(d1)*d2/(S*sqr(vol)*tau); + break; + case types::Theta: + val = rf*S*pnorm(pm*d1) + + pm*S*dnorm(d1)*(log(S/K)/(vol*sqrt(tau))-0.5*d1)/tau; + break; + case types::Vega: + val = -pm*S*dnorm(d1)*d2/vol; + break; + case types::Volga: + val = pm*S*dnorm(d1)/(vol*vol)*(-d1*d2*d2+d1+d2); + break; + case types::Vanna: + val = pm*dnorm(d1)/(vol*vol*sqrt(tau))*(d2*d2-1.0); + break; + case types::Rho_d: + val = pm*S*dnorm(d1)*sqrt(tau)/vol; + break; + case types::Rho_f: + val = -tau*S*pnorm(pm*d1) - pm*S*dnorm(d1)*sqrt(tau)/vol; + break; + default: + printf("binasset: greek %d not implemented\n", greeks ); + abort(); + } + } + return exp(-rf*tau)*val; +} + +// just for convenience we can combine bincash and binasset into +// one function binary +// using bincash() if fd==types::Domestic +// using binasset() if fd==types::Foreign +static double binary(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + double val=0.0; + switch(fd) { + case types::Domestic: + val = bincash(S,vol,rd,rf,tau,K,pc,greek); + break; + case types::Foreign: + val = binasset(S,vol,rd,rf,tau,K,pc,greek); + break; + default: + // never get here + assert(false); + } + return val; +} + +// further wrapper to combine single/double barrier binary options +// into one function +// B1<=0 - it is assumed lower barrier not set +// B2<=0 - it is assumed upper barrier not set +static double binary(double S, double vol, double rd, double rf, + double tau, double B1, double B2, + types::ForDom fd, types::Greeks greek) { + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + + double val=0.0; + + if(B1<=0.0 && B2<=0.0) { + // no barriers set, payoff 1.0 (domestic) or S_T (foreign) + val = binary(S,vol,rd,rf,tau,0.0,types::Call,fd,greek); + } else if(B1<=0.0 && B2>0.0) { + // upper barrier (put) + val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek); + } else if(B1>0.0 && B2<=0.0) { + // lower barrier (call) + val = binary(S,vol,rd,rf,tau,B1,types::Call,fd,greek); + } else if(B1>0.0 && B2>0.0) { + // double barrier + if(B2<=B1) { + val = 0.0; + } else { + val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek) + - binary(S,vol,rd,rf,tau,B1,types::Put,fd,greek); + } + } else { + // never get here + assert(false); + } + + return val; +} + +// vanilla put/call option +// call pays (S_T-K)^+ +// put pays (K-S_T)^+ +// this is the same as: +/- (binasset - K*bincash) +double putcall(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall putcall, types::Greeks greeks) { + + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + + double val = 0.0; + int pm = (putcall==types::Call) ? 1 : -1; + + if(K==0 || tau==0.0) { + // special cases, simply refer to binasset() and bincash() + val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks) + - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) ); + } else { + // general case + // we could just use pm*(binasset-K*bincash), however + // since the formula for delta and gamma simplify we write them + // down here + double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); + double d2 = d1 - vol*sqrt(tau); + + switch(greeks) { + case types::Value: + val = pm * ( exp(-rf*tau)*S*pnorm(pm*d1)-exp(-rd*tau)*K*pnorm(pm*d2) ); + break; + case types::Delta: + val = pm*exp(-rf*tau)*pnorm(pm*d1); + break; + case types::Gamma: + val = exp(-rf*tau)*dnorm(d1)/(S*vol*sqrt(tau)); + break; + default: + // too lazy for the other greeks, so simply refer to binasset/bincash + val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks) + - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) ); + } + } + return val; +} + +// truncated put/call option, single barrier +// need to specify whether it's down-and-out or up-and-out +// regular (keeps monotonicity): down-and-out for call, up-and-out for put +// reverse (destroys monoton): up-and-out for call, down-and-out for put +// call pays (S_T-K)^+ +// put pays (K-S_T)^+ +double putcalltrunc(double S, double vol, double rd, double rf, + double tau, double K, double B, + types::PutCall pc, types::KOType kotype, + types::Greeks greeks) { + + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + assert(B>=0.0); + + int pm = (pc==types::Call) ? 1 : -1; + double val = 0.0; + + switch(kotype) { + case types::Regular: + if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) { + // option degenerates to standard plain vanilla call/put + val = putcall(S,vol,rd,rf,tau,K,pc,greeks); + } else { + // normal case with truncation + val = pm * ( binasset(S,vol,rd,rf,tau,B,pc,greeks) + - K*bincash(S,vol,rd,rf,tau,B,pc,greeks) ); + } + break; + case types::Reverse: + if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) { + // option degenerates to zero payoff + val = 0.0; + } else { + // normal case with truncation + val = binasset(S,vol,rd,rf,tau,K,types::Call,greeks) + - binasset(S,vol,rd,rf,tau,B,types::Call,greeks) + - K * ( bincash(S,vol,rd,rf,tau,K,types::Call,greeks) + - bincash(S,vol,rd,rf,tau,B,types::Call,greeks) ); + } + break; + default: + assert(false); + } + return val; +} + +// wrapper function for put/call option which combines +// double/single/no truncation barrier +// B1<=0 - assume no lower barrier +// B2<=0 - assume no upper barrier +double putcalltrunc(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::Greeks greek) { + + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + + double val=0.0; + + if(B1<=0.0 && B2<=0.0) { + // no barriers set, plain vanilla + val = putcall(S,vol,rd,rf,tau,K,pc,greek); + } else if(B1<=0.0 && B2>0.0) { + // upper barrier: reverse barrier for call, regular barrier for put + if(pc==types::Call) { + val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Reverse,greek); + } else { + val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek); + } + } else if(B1>0.0 && B2<=0.0) { + // lower barrier: regular barrier for call, reverse barrier for put + if(pc==types::Call) { + val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek); + } else { + val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Reverse,greek); + } + } else if(B1>0.0 && B2>0.0) { + // double barrier + if(B2<=B1) { + val = 0.0; + } else { + int pm = (pc==types::Call) ? 1 : -1; + val = pm * ( + putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek) + - putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek) + ); + } + } else { + // never get here + assert(false); + } + return val; +} + +namespace internal { + +// wrapper function for all non-path dependent options +// this is only an internal function, used to avoid code duplication when +// going to path-dependent barrier options, +// K<0 - assume binary option +// K>=0 - assume put/call option +static double vanilla(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + double val = 0.0; + if(K<0.0) { + // binary option if K<0 + val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek); + } else { + val = putcall(S,vol,rd,rf,tau,K,pc,greek); + } + return val; +} +static double vanilla_trunc(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + double val = 0.0; + if(K<0.0) { + // binary option if K<0 + // truncated is actually the same as the vanilla binary + val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek); + } else { + val = putcalltrunc(S,vol,rd,rf,tau,K,B1,B2,pc,greek); + } + return val; +} + +} // namespace internal + +// path dependent options + + +namespace internal { + +// helper term for any type of options with continuously monitored barriers, +// internal, should not be called from outside +// calculates value and greeks based on +// V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S) +// (a=2 mu/vol^2, mu drift in logspace, ie. mu=(rd-rf-1/2vol^2)) +// with sc=1 and V1() being the price of the respective truncated +// vanilla option, V() would be the price of the respective barrier +// option if only one barrier is present +static double barrier_term(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, double sc, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + + // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S) + double val = 0.0; + double B = (B1>0.0) ? B1 : B2; + double a = 2.0*(rd-rf)/(vol*vol)-1.0; // helper variable + double b = 4.0*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol + double c = 12.0*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol + switch(greek) { + case types::Value: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + break; + case types::Delta: + val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + + pow(B/S,a) * ( + a/S* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + sqr(B/S)*sc* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + ); + break; + case types::Gamma: + val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + a*(a+1.0)/(S*S)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + (2.0*a+2.0)*B*B/(S*S*S)*sc* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta) + + sqr(sqr(B/S))*sc*sc* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Gamma) + ); + break; + case types::Theta: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + break; + case types::Vega: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + - b*log(B/S)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + 1.0* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + ); + break; + case types::Volga: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + log(B/S)*(b*b*log(B/S)+c)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + - 2.0*b*log(B/S)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega) + + 1.0* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga) + ); + break; + case types::Vanna: + val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + b/S*(log(B/S)*a+1.0)* + vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + b*log(B/S)*sqr(B/S)*sc* + vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta) + - a/S* + vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega) + - sqr(B/S)*sc* + vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna) + ); + break; + case types::Rho_d: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + 2.0*log(B/S)/(vol*vol)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + 1.0* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + ); + break; + case types::Rho_f: + val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - pow(B/S,a) * ( + - 2.0*log(B/S)/(vol*vol)* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) + + 1.0* + vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + ); + break; + default: + printf("barrier_term: greek %d not implemented\n", greek ); + abort(); + } + return val; +} + +// one term of the infinite sum for the valuation of double barriers +static double barrier_double_term( double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + double fac, double sc, int i, + types::PutCall pc, types::ForDom fd, types::Greeks greek) { + + double val = 0.0; + double b = 4.0*i*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol + double c = 12.0*i*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol + switch(greek) { + case types::Value: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); + break; + case types::Delta: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); + break; + case types::Gamma: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); + break; + case types::Theta: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); + break; + case types::Vega: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) + - b*log(B2/B1)*fac * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); + break; + case types::Volga: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) + - 2.0*b*log(B2/B1)*fac * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Vega) + + log(B2/B1)*fac*(c+b*b*log(B2/B1)) * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); + break; + case types::Vanna: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) + - b*log(B2/B1)*fac * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta); + break; + case types::Rho_d: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) + + 2.0*i/(vol*vol)*log(B2/B1)*fac * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); + break; + case types::Rho_f: + val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) + - 2.0*i/(vol*vol)*log(B2/B1)*fac * + barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); + break; + default: + printf("barrier_double_term: greek %d not implemented\n", greek ); + abort(); + } + return val; +} + +// general pricer for any type of options with continuously monitored barriers +// allows two, one or zero barriers, only knock-out style +// payoff profiles allowed based on vanilla_trunc() +static double barrier_ko(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + + double val = 0.0; + + if(B1<=0.0 && B2<=0.0) { + // no barriers --> vanilla case + val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if(B1>0.0 && B2<=0.0) { + // lower barrier + if(S<=B1) { + val = 0.0; // knocked out + } else { + val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek); + } + } else if(B1<=0.0 && B2>0.0) { + // upper barrier + if(S>=B2) { + val = 0.0; // knocked out + } else { + val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek); + } + } else if(B1>0.0 && B2>0.0) { + // double barrier + if(S<=B1 || S>=B2) { + val = 0.0; // knocked out (always true if wrong input B1>B2) + } else { + // more complex calculation as we have to evaluate an infinite + // sum + // to reduce very costly pow() calls we define some variables + double a = 2.0*(rd-rf)/(vol*vol)-1.0; // 2 (mu-1/2vol^2)/sigma^2 + double BB2=sqr(B2/B1); + double BBa=pow(B2/B1,a); + double BB2inv=1.0/BB2; + double BBainv=1.0/BBa; + double fac=1.0; + double facinv=1.0; + double sc=1.0; + double scinv=1.0; + + // initial term i=0 + val=barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,0,pc,fd,greek); + // infinite loop, 10 should be plenty, normal would be 2 + for(int i=1; i<10; i++) { + fac*=BBa; + facinv*=BBainv; + sc*=BB2; + scinv*=BB2inv; + double add = + barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,i,pc,fd,greek) + + barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,facinv,scinv,-i,pc,fd,greek); + val += add; + //printf("%i: val=%e (add=%e)\n",i,val,add); + if(fabs(add) <= 1e-12*fabs(val)) { + break; + } + } + // not knocked-out double barrier end + } + // double barrier end + } else { + // no such barrier combination exists + assert(false); + } + + return val; +} + +// knock-in style barrier +static double barrier_ki(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::ForDom fd, + types::Greeks greek) { + return vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + -barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); +} + +// general barrier +static double barrier(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::ForDom fd, + types::BarrierKIO kio, types::BarrierActive bcont, + types::Greeks greek) { + + double val = 0.0; + if( kio==types::KnockOut && bcont==types::Maturity ) { + // truncated vanilla option + val = vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockOut && bcont==types::Continuous ) { + // standard knock-out barrier + val = barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockIn && bcont==types::Maturity ) { + // inverse truncated vanilla + val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) + - vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockIn && bcont==types::Continuous ) { + // standard knock-in barrier + val = barrier_ki(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else { + // never get here + assert(false); + } + return val; +} + +} // namespace internal + + +// touch/no-touch options (cash/asset or nothing payoff profile) +double touch(double S, double vol, double rd, double rf, + double tau, double B1, double B2, types::ForDom fd, + types::BarrierKIO kio, types::BarrierActive bcont, + types::Greeks greek) { + + double K=-1.0; // dummy + types::PutCall pc = types::Call; // dummy + double val = 0.0; + if( kio==types::KnockOut && bcont==types::Maturity ) { + // truncated vanilla option + val = internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockOut && bcont==types::Continuous ) { + // standard knock-out barrier + val = internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockIn && bcont==types::Maturity ) { + // inverse truncated vanilla + val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek) + - internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else if ( kio==types::KnockIn && bcont==types::Continuous ) { + // standard knock-in barrier + val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek) + - internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); + } else { + // never get here + assert(false); + } + return val; +} + +// barrier option (put/call payoff profile) +double barrier(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + double rebate, + types::PutCall pc, types::BarrierKIO kio, + types::BarrierActive bcont, + types::Greeks greek) { + assert(tau>=0.0); + assert(S>0.0); + assert(vol>0.0); + assert(K>=0.0); + types::ForDom fd = types::Domestic; + double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek); + if(rebate!=0.0) { + // opposite of barrier knock-in/out type + types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut + : types::KnockIn; + val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek); + } + return val; +} + +// probability of hitting a barrier +// this is almost the same as the price of a touch option (domestic) +// as it pays one if a barrier is hit; we only have to offset the +// discounting and we get the probability +double prob_hit(double S, double vol, double mu, + double tau, double B1, double B2) { + double const rd=0.0; + double rf=-mu; + return 1.0 - touch(S,vol,rd,rf,tau,B1,B2,types::Domestic,types::KnockOut, + types::Continuous, types::Value); +} + +// probability of being in-the-money, ie payoff is greater zero, +// assuming payoff(S_T) > 0 iff S_T in [B1, B2] +// this the same as the price of a cash or nothing option +// with no discounting +double prob_in_money(double S, double vol, double mu, + double tau, double B1, double B2) { + assert(S>0.0); + assert(vol>0.0); + assert(tau>=0.0); + double val = 0.0; + if( B1<B2 || B1<=0.0 || B2<=0.0 ) { + val = binary(S,vol,0.0,-mu,tau,B1,B2,types::Domestic,types::Value); + } + return val; +} +double prob_in_money(double S, double vol, double mu, + double tau, double K, double B1, double B2, + types::PutCall pc) { + assert(S>0.0); + assert(vol>0.0); + assert(tau>=0.0); + + // if K<0 we assume a binary option is given + if(K<0.0) { + return prob_in_money(S,vol,mu,tau,B1,B2); + } + + double val = 0.0; + double BM1, BM2; // range of in the money [BM1, BM2] + // non-sense parameters with no positive payoff + if( (B1>B2 && B1>0.0 && B2>0.0) || + (K>=B2 && B2>0.0 && pc==types::Call) || + (K<=B1 && pc==types::Put) ) { + val = 0.0; + // need to figure out between what barriers payoff is greater 0 + } else if(pc==types::Call) { + BM1=std::max(B1, K); + BM2=B2; + val = prob_in_money(S,vol,mu,tau,BM1,BM2); + } else if (pc==types::Put) { + BM1=B1; + BM2= (B2>0.0) ? std::min(B2,K) : K; + val = prob_in_money(S,vol,mu,tau,BM1,BM2); + } else { + // don't get here + assert(false); + } + return val; +} + +} // namespace sca + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/pricing/black_scholes.hxx b/scaddins/source/pricing/black_scholes.hxx new file mode 100644 index 000000000..cdcb20b84 --- /dev/null +++ b/scaddins/source/pricing/black_scholes.hxx @@ -0,0 +1,149 @@ +/* -*- 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/. + * + * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de> + * + */ + + +#ifndef INCLUDED_SCADDINS_SOURCE_PRICING_BLACK_SCHOLES_HXX +#define INCLUDED_SCADDINS_SOURCE_PRICING_BLACK_SCHOLES_HXX + +// options prices and greeks in the Black-Scholes model +// also known as TV (theoretical value) + +namespace sca { +namespace pricing { + +namespace bs { + +namespace types { +enum Greeks { + Value = 0, + Delta = 1, // d/dS + Gamma = 2, // d^2/dS^2 + Theta = 3, // d/dt + Vega = 4, // d/dsigma + Volga = 5, // d^2/dsigma^2 + Vanna = 6, // d^2/dsigma dS + Rho_d = 7, // d/dr_d + Rho_f = 8 // d/dr_f +}; + +enum PutCall { + Call = 1, + Put = -1 +}; + +enum KOType { + Regular = 0, + Reverse = 1 +}; + +enum BarrierKIO { + KnockIn = -1, + KnockOut = 1 +}; + +// barrier observed continuously or just at maturity (truncated payoff) +enum BarrierActive { + Continuous = 0, + Maturity = 1 +}; + +enum ForDom { + Domestic = 0, + Foreign = 1 +}; + +} // namespace types + + +// binary option cash (domestic) +// call - pays 1 if S_T is above strike K +// put - pays 1 if S_T is below strike K +double bincash(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall pc, types::Greeks greeks); + +// binary option asset (foreign) +// call - pays S_T if S_T is above strike K +// put - pays S_T if S_T is below strike K +double binasset(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall pc, types::Greeks greeks); + +// vanilla put/call option +// call pays (S_T-K)^+ +// put pays (K-S_T)^+ +// this is the same as: +/- (binasset - K*bincash) +double putcall(double S, double vol, double rd, double rf, + double tau, double K, + types::PutCall putcall, types::Greeks greeks); + + +// truncated put/call option, single barrier +// need to specify whether it's down-and-out or up-and-out +// regular (keeps monotonicity): down-and-out for call, up-and-out for put +// reverse (destroys monoton): up-and-out for call, down-and-out for put +// call pays (S_T-K)^+ +// put pays (K-S_T)^+ +double putcalltrunc(double S, double vol, double rd, double rf, + double tau, double K, double B, + types::PutCall pc, types::KOType kotype, + types::Greeks greeks); + + +// wrapper function for put/call option which combines +// double/single/no truncation barrier +// B1<=0 - assume no lower barrier +// B2<=0 - assume no upper barrier +double putcalltrunc(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + types::PutCall pc, types::Greeks greek); + +// barrier +// touch/no-touch options (cash/asset or nothing payoff profile) +double touch(double S, double vol, double rd, double rf, + double tau, double B1, double B2, types::ForDom fd, + types::BarrierKIO kio, types::BarrierActive bcont, + types::Greeks greek); + +// barrier +// barrier option (put/call payoff profile) +double barrier(double S, double vol, double rd, double rf, + double tau, double K, double B1, double B2, + double rebate, + types::PutCall pc, types::BarrierKIO kio, + types::BarrierActive bcont, + types::Greeks greek); + + +// probability of hitting a barrier +double prob_hit(double S, double vol, double mu, + double tau, double B1, double B2); + + +// probability of being in-the-money, ie payoff is greater zero, +// assuming payoff(S_T) > 0 iff S_T in [B1, B2] +double prob_in_money(double S, double vol, double mu, + double tau, double B1, double B2); +double prob_in_money(double S, double vol, double mu, + double tau, double K, double B1, double B2, + types::PutCall pc); + + +} // namespace bs + +} // namespace pricing +} // namespace sca + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/pricing/pricing.component b/scaddins/source/pricing/pricing.component new file mode 100644 index 000000000..6083144a4 --- /dev/null +++ b/scaddins/source/pricing/pricing.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="pricing" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sheet.addin.PricingFunctionsImpl"> + <service name="com.sun.star.sheet.AddIn"/> + <service name="com.sun.star.sheet.addin.PricingFunctions"/> + </implementation> +</component> diff --git a/scaddins/source/pricing/pricing.cxx b/scaddins/source/pricing/pricing.cxx new file mode 100644 index 000000000..32f4414dd --- /dev/null +++ b/scaddins/source/pricing/pricing.cxx @@ -0,0 +1,545 @@ +/* -*- 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 . + */ + +// pricing functions add in + +// all of the UNO add-in technical details have been copied from +// ../datefunc/datefunc.cxx + +#include "pricing.hxx" +#include "black_scholes.hxx" +#include <pricing.hrc> +#include <strings.hrc> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <iostream> +#include <algorithm> +#include <rtl/math.hxx> +#include <unotools/resmgr.hxx> +#include <i18nlangtag/languagetag.hxx> + +using namespace ::com::sun::star; +using namespace sca::pricing; + + +#define ADDIN_SERVICE "com.sun.star.sheet.AddIn" +#define MY_SERVICE "com.sun.star.sheet.addin.PricingFunctions" +#define MY_IMPLNAME "com.sun.star.sheet.addin.PricingFunctionsImpl" + +#define UNIQUE false // function name does not exist in Calc + +#define STDPAR false // all parameters are described + +#define FUNCDATA( FuncName, CompName, ParamCount, Category, Double, IntPar ) \ + { "get" #FuncName, PRICING_FUNCNAME_##FuncName, PRICING_FUNCDESC_##FuncName, CompName, ParamCount, Category, Double, IntPar } + +const ScaFuncDataBase pFuncDataArr[] = +{ + FUNCDATA(OptBarrier, "OPT_BARRIER", 13, ScaCategory::Finance, UNIQUE, STDPAR), + FUNCDATA(OptTouch, "OPT_TOUCH", 11, ScaCategory::Finance, UNIQUE, STDPAR), + FUNCDATA(OptProbHit, "OPT_PROB_HIT", 6, ScaCategory::Finance, UNIQUE, STDPAR), + FUNCDATA(OptProbInMoney, "OPT_PROB_INMONEY", 8, ScaCategory::Finance, UNIQUE, STDPAR) +}; + +#undef FUNCDATA + +ScaFuncData::ScaFuncData( const ScaFuncDataBase& rBaseData ) : + aIntName( OUString::createFromAscii( rBaseData.pIntName ) ), + pUINameID( rBaseData.pUINameID ), + pDescrID( rBaseData.pDescrID ), + nParamCount( rBaseData.nParamCount ), + eCat( rBaseData.eCat ), + bDouble( rBaseData.bDouble ), + bWithOpt( rBaseData.bWithOpt ) +{ + aCompList.push_back(OUString::createFromAscii(rBaseData.pCompName)); +} + +sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const +{ + if( !bWithOpt ) + nParam++; + return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2); +} + +void sca::pricing::InitScaFuncDataList(ScaFuncDataList& rList) +{ + for (const auto & nIndex : pFuncDataArr) + rList.push_back(ScaFuncData(nIndex)); +} + +// entry points for service registration / instantiation +static uno::Reference< uno::XInterface > ScaPricingAddIn_CreateInstance( + const uno::Reference< lang::XMultiServiceFactory >& ) +{ + return static_cast<cppu::OWeakObject*>(new ScaPricingAddIn()); +} + +extern "C" { + +SAL_DLLPUBLIC_EXPORT void * pricing_component_getFactory( + const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void* pRet = nullptr; + + if ( pServiceManager && + OUString::createFromAscii( pImplName ) == ScaPricingAddIn::getImplementationName_Static() ) + { + uno::Reference< lang::XSingleServiceFactory > xFactory( cppu::createOneInstanceFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + ScaPricingAddIn::getImplementationName_Static(), + ScaPricingAddIn_CreateInstance, + ScaPricingAddIn::getSupportedServiceNames_Static() ) ); + + if (xFactory.is()) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +} // extern C + +// "normal" service implementation +ScaPricingAddIn::ScaPricingAddIn() +{ +} + +ScaPricingAddIn::~ScaPricingAddIn() +{ +} + +static const char* pLang[] = { "de", "en" }; +static const char* pCoun[] = { "DE", "US" }; +static const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS( pLang ); + +void ScaPricingAddIn::InitDefLocales() +{ + pDefLocales.reset( new lang::Locale[ nNumOfLoc ] ); + + for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ ) + { + pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] ); + pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] ); + } +} + +const lang::Locale& ScaPricingAddIn::GetLocale( sal_uInt32 nIndex ) +{ + if( !pDefLocales ) + InitDefLocales(); + + return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc; +} + +void ScaPricingAddIn::InitData() +{ + aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc)); + pFuncDataList.reset(new ScaFuncDataList); + InitScaFuncDataList(*pFuncDataList); + pDefLocales.reset(); +} + +OUString ScaPricingAddIn::GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex) +{ + return ScaResId(pResId[nStrIndex - 1]); +} + +OUString ScaPricingAddIn::getImplementationName_Static() +{ + return MY_IMPLNAME; +} + +uno::Sequence< OUString > ScaPricingAddIn::getSupportedServiceNames_Static() +{ + return { ADDIN_SERVICE, MY_SERVICE }; +} + +// XServiceName +OUString SAL_CALL ScaPricingAddIn::getServiceName() +{ + // name of specific AddIn service + return MY_SERVICE; +} + +// XServiceInfo +OUString SAL_CALL ScaPricingAddIn::getImplementationName() +{ + return getImplementationName_Static(); +} + +sal_Bool SAL_CALL ScaPricingAddIn::supportsService( const OUString& aServiceName ) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence< OUString > SAL_CALL ScaPricingAddIn::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +// XLocalizable +void SAL_CALL ScaPricingAddIn::setLocale( const lang::Locale& eLocale ) +{ + aFuncLoc = eLocale; + InitData(); // change of locale invalidates resources! +} + +lang::Locale SAL_CALL ScaPricingAddIn::getLocale() +{ + return aFuncLoc; +} + +// function descriptions start here +// XAddIn +OUString SAL_CALL ScaPricingAddIn::getProgrammaticFuntionName( const OUString& ) +{ + // not used by calc + // (but should be implemented for other uses of the AddIn service) + return OUString(); +} + +OUString SAL_CALL ScaPricingAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if (fDataIt != pFuncDataList->end() ) + { + aRet = ScaResId(fDataIt->GetUINameID()); + if( fDataIt->IsDouble() ) + aRet += "_ADD"; + } + else + { + aRet = "UNKNOWNFUNC_" + aProgrammaticName; + } + + return aRet; +} + +OUString SAL_CALL ScaPricingAddIn::getFunctionDescription( const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), 1 ); + + return aRet; +} + +OUString SAL_CALL ScaPricingAddIn::getDisplayArgumentName( + const OUString& aProgrammaticName, sal_Int32 nArgument ) +{ + OUString aRet; + + auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) ) + { + sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); + if( nStr ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr ); + else + aRet = "internal"; + } + + return aRet; +} + +OUString SAL_CALL ScaPricingAddIn::getArgumentDescription( + const OUString& aProgrammaticName, sal_Int32 nArgument ) +{ + OUString aRet; + + auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) ) + { + sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); + if( nStr ) + aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr + 1 ); + else + aRet = "for internal use only"; + } + + return aRet; +} + +OUString SAL_CALL ScaPricingAddIn::getProgrammaticCategoryName( + const OUString& aProgrammaticName ) +{ + OUString aRet; + + auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt != pFuncDataList->end() ) + { + switch( fDataIt->GetCategory() ) + { + case ScaCategory::DateTime: aRet = "Date&Time"; break; + case ScaCategory::Text: aRet = "Text"; break; + case ScaCategory::Finance: aRet = "Financial"; break; + case ScaCategory::Inf: aRet = "Information"; break; + case ScaCategory::Math: aRet = "Mathematical"; break; + case ScaCategory::Tech: aRet = "Technical"; break; + } + } + + if( aRet.isEmpty() ) + aRet = "Add-In"; + return aRet; +} + +OUString SAL_CALL ScaPricingAddIn::getDisplayCategoryName( + const OUString& aProgrammaticName ) +{ + return getProgrammaticCategoryName( aProgrammaticName ); +} + +// XCompatibilityNames +uno::Sequence< sheet::LocalizedName > SAL_CALL ScaPricingAddIn::getCompatibilityNames( + const OUString& aProgrammaticName ) +{ + auto fDataIt = std::find_if( pFuncDataList->begin(), pFuncDataList->end(), + FindScaFuncData( aProgrammaticName ) ); + if( fDataIt == pFuncDataList->end() ) + return uno::Sequence< sheet::LocalizedName >( 0 ); + + const std::vector<OUString>& rStrList = fDataIt->GetCompNameList(); + sal_uInt32 nCount = rStrList.size(); + + uno::Sequence< sheet::LocalizedName > aRet( nCount ); + sheet::LocalizedName* pArray = aRet.getArray(); + + for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ ) + pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), rStrList[nIndex] ); + + return aRet; +} + +// actual function implementation starts here +// auxiliary input handling functions +namespace { + +bool getinput_putcall(bs::types::PutCall& pc, const OUString& str) { + if(str.startsWith("c")) { + pc=bs::types::Call; + } else if(str.startsWith("p")) { + pc=bs::types::Put; + } else { + return false; + } + return true; +} + +bool getinput_putcall(bs::types::PutCall& pc, const uno::Any& anyval) { + OUString str; + if(anyval.getValueTypeClass() == uno::TypeClass_STRING) { + anyval >>= str; + } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) { + str="c"; // call as default + } else { + return false; + } + return getinput_putcall(pc, str); +} + +bool getinput_strike(double& strike, const uno::Any& anyval) { + if(anyval.getValueTypeClass() == uno::TypeClass_DOUBLE) { + anyval >>= strike; + } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) { + strike=-1.0; // -1 as default (means not set) + } else { + return false; + } + return true; +} + +bool getinput_inout(bs::types::BarrierKIO& kio, const OUString& str) { + if(str.startsWith("i")) { + kio=bs::types::KnockIn; + } else if(str.startsWith("o")) { + kio=bs::types::KnockOut; + } else { + return false; + } + return true; +} + +bool getinput_barrier(bs::types::BarrierActive& cont, const OUString& str) { + if(str.startsWith("c")) { + cont=bs::types::Continuous; + } else if(str.startsWith("e")) { + cont=bs::types::Maturity; + } else { + return false; + } + return true; +} + +bool getinput_fordom(bs::types::ForDom& fd, const OUString& str) { + if(str.startsWith("f")) { + fd=bs::types::Foreign; + } else if(str.startsWith("d")) { + fd=bs::types::Domestic; + } else { + return false; + } + return true; +} + +bool getinput_greek(bs::types::Greeks& greek, const uno::Any& anyval) { + OUString str; + if(anyval.getValueTypeClass() == uno::TypeClass_STRING) { + anyval >>= str; + } else if(anyval.getValueTypeClass() == uno::TypeClass_VOID) { + str="value"; + } else { + return false; + } + + if(str == "value" || str == "price" || str == "v" || str == "p") { + greek=bs::types::Value; + } else if(str == "delta" || str == "d") { + greek=bs::types::Delta; + } else if(str == "gamma" || str == "g") { + greek=bs::types::Gamma; + } else if(str == "theta" || str == "t") { + greek=bs::types::Theta; + } else if(str == "vega" || str == "e") { + greek=bs::types::Vega; + } else if(str == "volga" || str == "o") { + greek=bs::types::Volga; + } else if(str == "vanna" || str == "a") { + greek=bs::types::Vanna; + } else if(str == "rho" || str == "r") { + greek=bs::types::Rho_d; + } else if(str == "rhof" || str == "f") { + greek=bs::types::Rho_f; + } else { + return false; + } + return true; +} + +} // namespace for auxiliary functions + +// OPT_BARRIER(...) +double SAL_CALL ScaPricingAddIn::getOptBarrier( double spot, double vol, + double r, double rf, double T, double strike, + double barrier_low, double barrier_up, double rebate, + const OUString& put_call, const OUString& in_out, + const OUString& barriercont, const uno::Any& greekstr ) +{ + bs::types::PutCall pc; + bs::types::BarrierKIO kio; + bs::types::BarrierActive bcont; + bs::types::Greeks greek; + // read and check input values + if( spot<=0.0 || vol<=0.0 || T<0.0 || strike<0.0 || + !getinput_putcall(pc,put_call) || + !getinput_inout(kio,in_out) || + !getinput_barrier(bcont,barriercont) || + !getinput_greek(greek,greekstr) ){ + throw lang::IllegalArgumentException(); + } + + double fRet=bs::barrier(spot,vol,r,rf,T,strike, barrier_low,barrier_up, + rebate,pc,kio,bcont,greek); + + RETURN_FINITE( fRet ); +} + +// OPT_TOUCH(...) +double SAL_CALL ScaPricingAddIn::getOptTouch( double spot, double vol, + double r, double rf, double T, + double barrier_low, double barrier_up, + const OUString& for_dom, const OUString& in_out, + const OUString& barriercont, const uno::Any& greekstr ) +{ + bs::types::ForDom fd; + bs::types::BarrierKIO kio; + bs::types::BarrierActive bcont; + bs::types::Greeks greek; + // read and check input values + if( spot<=0.0 || vol<=0.0 || T<0.0 || + !getinput_fordom(fd,for_dom) || + !getinput_inout(kio,in_out) || + !getinput_barrier(bcont,barriercont) || + !getinput_greek(greek,greekstr) ){ + throw lang::IllegalArgumentException(); + } + + double fRet=bs::touch(spot,vol,r,rf,T,barrier_low,barrier_up, + fd,kio,bcont,greek); + + RETURN_FINITE( fRet ); +} + +// OPT_PRB_HIT(...) +double SAL_CALL ScaPricingAddIn::getOptProbHit( double spot, double vol, + double mu, double T, + double barrier_low, double barrier_up ) +{ + // read and check input values + if( spot<=0.0 || vol<=0.0 || T<0.0 ) { + throw lang::IllegalArgumentException(); + } + + double fRet=bs::prob_hit(spot,vol,mu,T,barrier_low,barrier_up); + + RETURN_FINITE( fRet ); +} + +// OPT_PROB_INMONEY(...) +double SAL_CALL ScaPricingAddIn::getOptProbInMoney( double spot, double vol, + double mu, double T, + double barrier_low, double barrier_up, + const uno::Any& strikeval, const uno::Any& put_call ) +{ + bs::types::PutCall pc=bs::types::Call; + double K = 0; + + // read and check input values + if( spot<=0.0 || vol<=0.0 || T<0.0 || + !getinput_putcall(pc,put_call) || + !getinput_strike(K,strikeval) ) { + throw lang::IllegalArgumentException(); + } + + double fRet=bs::prob_in_money(spot,vol,mu,T,K,barrier_low,barrier_up,pc); + + RETURN_FINITE( fRet ); +} + +OUString ScaPricingAddIn::ScaResId(const char* pResId) +{ + return Translate::get(pResId, aResLocale); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scaddins/source/pricing/pricing.hxx b/scaddins/source/pricing/pricing.hxx new file mode 100644 index 000000000..8acbfd4a8 --- /dev/null +++ b/scaddins/source/pricing/pricing.hxx @@ -0,0 +1,209 @@ +/* -*- 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 . + */ + +// option pricing functions add in + +// most parts of this files are technical UNO details which are +// all copied from ../datefunc/datefunc.hxx +// to avoid having to rename all classes to do with UNO +// technicalities we use our own namespace + +#ifndef INCLUDED_SCADDINS_SOURCE_PRICING_PRICING_HXX +#define INCLUDED_SCADDINS_SOURCE_PRICING_PRICING_HXX + + +#include <vector> +#include <memory> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sheet/XAddIn.hpp> +#include <com/sun/star/sheet/XCompatibilityNames.hpp> +#include <com/sun/star/sheet/addin/XPricingFunctions.hpp> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::lang { class XMultiServiceFactory; } + +#define RETURN_FINITE(d) if( !std::isfinite( d ) ) throw css::lang::IllegalArgumentException(); return d; + + +namespace sca { +namespace pricing { + +enum class ScaCategory +{ + DateTime, + Text, + Finance, + Inf, + Math, + Tech +}; + +struct ScaFuncDataBase +{ + const char* pIntName; // internal name (get***) + const char* pUINameID; // resource ID to UI name + const char** pDescrID; // resource ID to description, parameter names and ~ description + // pCompName was originally meant to be able to load Excel documents that for + // some time were stored with localized function names. + // This is not relevant to this add-in, so we only supply the same + // (English) function names again. + // see also: GetExcelName() or GetCompNames() or getCompatibilityNames() + const char* pCompName; + sal_uInt16 nParamCount; // number of named / described parameters + ScaCategory eCat; // function category + bool bDouble; // name already exist in Calc + bool bWithOpt; // first parameter is internal +}; + +class ScaFuncData final +{ +private: + OUString aIntName; // internal name (get***) + const char* pUINameID; // resource ID to UI name + const char** pDescrID; // leads also to parameter descriptions! + sal_uInt16 nParamCount; // num of parameters + std::vector<OUString> aCompList; // list of all valid names + ScaCategory eCat; // function category + bool bDouble; // name already exist in Calc + bool bWithOpt; // first parameter is internal + +public: + ScaFuncData(const ScaFuncDataBase& rBaseData); + + const char* GetUINameID() const { return pUINameID; } + const char** GetDescrID() const { return pDescrID; } + ScaCategory GetCategory() const { return eCat; } + bool IsDouble() const { return bDouble; } + + sal_uInt16 GetStrIndex( sal_uInt16 nParam ) const; + bool Is( const OUString& rCompare ) const + { return aIntName == rCompare; } + + const std::vector<OUString>& GetCompNameList() const { return aCompList; } +}; + + +typedef std::vector<ScaFuncData> ScaFuncDataList; + +void InitScaFuncDataList(ScaFuncDataList& rMap); + +// Predicate for use with std::find_if +struct FindScaFuncData +{ + const OUString& m_rId; + explicit FindScaFuncData( const OUString& rId ) : m_rId(rId) {} + bool operator() ( ScaFuncData const & rCandidate ) const { return rCandidate.Is(m_rId); } +}; + +} // namespace pricing +} // namespace sca + + +css::uno::Reference< css::uno::XInterface > SAL_CALL PricingFunctionAddIn_CreateInstance( + const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + + +// AddIn class for pricing functions + +class ScaPricingAddIn : public ::cppu::WeakImplHelper< + css::sheet::XAddIn, + css::sheet::XCompatibilityNames, + css::sheet::addin::XPricingFunctions, + css::lang::XServiceName, + css::lang::XServiceInfo > +{ +private: + css::lang::Locale aFuncLoc; + std::unique_ptr<css::lang::Locale[]> pDefLocales; + std::locale aResLocale; + std::unique_ptr<sca::pricing::ScaFuncDataList> pFuncDataList; + + + void InitDefLocales(); + const css::lang::Locale& GetLocale( sal_uInt32 nIndex ); + void InitData(); + + /// @throws css::uno::RuntimeException + OUString GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex); + +public: + ScaPricingAddIn(); + virtual ~ScaPricingAddIn() override; + + OUString ScaResId(const char* pResId); + + static OUString getImplementationName_Static(); + static css::uno::Sequence< OUString > getSupportedServiceNames_Static(); + + // XAddIn + virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) override; + virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getDisplayArgumentName( const OUString& aProgrammaticName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getArgumentDescription( const OUString& aProgrammaticName, sal_Int32 nArgument ) override; + virtual OUString SAL_CALL getProgrammaticCategoryName( const OUString& aProgrammaticName ) override; + virtual OUString SAL_CALL getDisplayCategoryName( const OUString& aProgrammaticName ) override; + + // XCompatibilityNames + virtual css::uno::Sequence< css::sheet::LocalizedName > SAL_CALL getCompatibilityNames( const OUString& aProgrammaticName ) override; + + // XLocalizable + virtual void SAL_CALL setLocale( const css::lang::Locale& eLocale ) override; + virtual css::lang::Locale SAL_CALL getLocale() override; + + // XServiceName + virtual OUString SAL_CALL getServiceName() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + + // methods from own interfaces start here + + + virtual double SAL_CALL getOptBarrier( double spot, double vol, + double r, double rf, double T, double strike, + double barrier_low, double barrier_up, double rebate, + const OUString& put_call, const OUString& in_out, + const OUString& continuous, const css::uno::Any& greek ) override; + + virtual double SAL_CALL getOptTouch( double spot, double vol, + double r, double rf, double T, + double barrier_low, double barrier_up, + const OUString& for_dom, const OUString& in_out, + const OUString& barriercont, const css::uno::Any& greekstr ) override; + + virtual double SAL_CALL getOptProbHit( double spot, double vol, + double mu, double T, + double barrier_low, double barrier_up ) override; + + virtual double SAL_CALL getOptProbInMoney( double spot, double vol, + double mu, double T, + double barrier_low, double barrier_up, + const css::uno::Any& strikeval, const css::uno::Any& put_call ) override; + +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |