1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "analysisdefs.hxx"
21 #include "analysis.hxx"
22 #include "bessel.hxx"
23 #include <cppuhelper/factory.hxx>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/random.hxx>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
28 #include <o3tl/any.hxx>
29 #include <rtl/math.hxx>
30 #include <sal/macros.h>
31 #include <unotools/resmgr.hxx>
32 #include <i18nlangtag/languagetag.hxx>
33 #include <algorithm>
34 #include <cmath>
35 #include <float.h>
36 
37 #define ADDIN_SERVICE               "com.sun.star.sheet.AddIn"
38 #define MY_SERVICE                  "com.sun.star.sheet.addin.Analysis"
39 #define MY_IMPLNAME                 "com.sun.star.sheet.addin.AnalysisImpl"
40 
41 using namespace                 ::com::sun::star;
42 using namespace sca::analysis;
43 using namespace std;
44 
analysis_component_getFactory(const sal_Char * pImplName,void * pServiceManager,void *)45 extern "C" SAL_DLLPUBLIC_EXPORT void* analysis_component_getFactory(
46     const sal_Char* pImplName, void* pServiceManager, void* /*pRegistryKey*/ )
47 {
48     void* pRet = nullptr;
49 
50     if( pServiceManager && OUString::createFromAscii( pImplName ) == AnalysisAddIn::getImplementationName_Static() )
51     {
52         uno::Reference< lang::XSingleServiceFactory >  xFactory( cppu::createOneInstanceFactory(
53                 static_cast< lang::XMultiServiceFactory* >( pServiceManager ),
54                 AnalysisAddIn::getImplementationName_Static(),
55                 AnalysisAddIn_CreateInstance,
56                 AnalysisAddIn::getSupportedServiceNames_Static() ) );
57 
58         if( xFactory.is() )
59         {
60             xFactory->acquire();
61             pRet = xFactory.get();
62         }
63     }
64 
65     return pRet;
66 }
67 
GetFuncDescrStr(const char ** pResId,sal_uInt16 nStrIndex)68 OUString AnalysisAddIn::GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex)
69 {
70     return AnalysisResId(pResId[nStrIndex - 1]);
71 }
72 
InitData()73 void AnalysisAddIn::InitData()
74 {
75     aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc));
76 
77     pFD.reset(new FuncDataList);
78     InitFuncDataList(*pFD);
79 
80     pDefLocales.reset();
81 }
82 
AnalysisAddIn(const uno::Reference<uno::XComponentContext> & xContext)83 AnalysisAddIn::AnalysisAddIn( const uno::Reference< uno::XComponentContext >& xContext ) :
84     aAnyConv( xContext )
85 {
86 }
87 
~AnalysisAddIn()88 AnalysisAddIn::~AnalysisAddIn()
89 {
90 }
91 
getDateMode(const uno::Reference<beans::XPropertySet> & xPropSet,const uno::Any & rAny)92 sal_Int32 AnalysisAddIn::getDateMode(
93         const uno::Reference< beans::XPropertySet >& xPropSet,
94         const uno::Any& rAny )
95 {
96     sal_Int32 nMode = aAnyConv.getInt32( xPropSet, rAny, 0 );
97     if( (nMode < 0) || (nMode > 4) )
98         throw lang::IllegalArgumentException();
99     return nMode;
100 }
101 
102 #define MAXFACTDOUBLE   300
103 
FactDouble(sal_Int32 nNum)104 double AnalysisAddIn::FactDouble( sal_Int32 nNum )
105 {
106     if( nNum < 0 || nNum > MAXFACTDOUBLE )
107         throw lang::IllegalArgumentException();
108 
109     if( !pFactDoubles )
110     {
111         pFactDoubles.reset( new double[ MAXFACTDOUBLE + 1 ] );
112 
113         pFactDoubles[ 0 ] = 1.0;    // by default
114 
115         double      fOdd = 1.0;
116         double      fEven = 2.0;
117 
118         pFactDoubles[ 1 ] = fOdd;
119         pFactDoubles[ 2 ] = fEven;
120 
121         bool    bOdd = true;
122 
123         for( sal_uInt16 nCnt = 3 ; nCnt <= MAXFACTDOUBLE ; nCnt++ )
124         {
125             if( bOdd )
126             {
127                 fOdd *= nCnt;
128                 pFactDoubles[ nCnt ] = fOdd;
129             }
130             else
131             {
132                 fEven *= nCnt;
133                 pFactDoubles[ nCnt ] = fEven;
134             }
135 
136             bOdd = !bOdd;
137 
138         }
139     }
140 
141     return pFactDoubles[ nNum ];
142 }
143 
getImplementationName_Static()144 OUString AnalysisAddIn::getImplementationName_Static()
145 {
146     return MY_IMPLNAME;
147 }
148 
getSupportedServiceNames_Static()149 uno::Sequence< OUString > AnalysisAddIn::getSupportedServiceNames_Static()
150 {
151     return { ADDIN_SERVICE, MY_SERVICE };
152 }
153 
AnalysisAddIn_CreateInstance(const uno::Reference<lang::XMultiServiceFactory> & xServiceFact)154 uno::Reference< uno::XInterface > AnalysisAddIn_CreateInstance(
155         const uno::Reference< lang::XMultiServiceFactory >& xServiceFact )
156 {
157     return static_cast<cppu::OWeakObject*>(new AnalysisAddIn( comphelper::getComponentContext(xServiceFact) ));
158 }
159 
160 // XServiceName
getServiceName()161 OUString SAL_CALL AnalysisAddIn::getServiceName()
162 {
163     // name of specific AddIn service
164     return MY_SERVICE;
165 }
166 
167 // XServiceInfo
getImplementationName()168 OUString SAL_CALL AnalysisAddIn::getImplementationName()
169 {
170     return getImplementationName_Static();
171 }
172 
supportsService(const OUString & aName)173 sal_Bool SAL_CALL AnalysisAddIn::supportsService( const OUString& aName )
174 {
175     return cppu::supportsService(this, aName);
176 }
177 
getSupportedServiceNames()178 uno::Sequence< OUString > SAL_CALL AnalysisAddIn::getSupportedServiceNames()
179 {
180     return getSupportedServiceNames_Static();
181 }
182 
183 // XLocalizable
setLocale(const lang::Locale & eLocale)184 void SAL_CALL AnalysisAddIn::setLocale( const lang::Locale& eLocale )
185 {
186     aFuncLoc = eLocale;
187 
188     InitData();     // change of locale invalidates resources!
189 }
190 
getLocale()191 lang::Locale SAL_CALL AnalysisAddIn::getLocale()
192 {
193     return aFuncLoc;
194 }
195 
196 // XAddIn
getProgrammaticFuntionName(const OUString &)197 OUString SAL_CALL AnalysisAddIn::getProgrammaticFuntionName( const OUString& )
198 {
199     //  not used by calc
200     //  (but should be implemented for other uses of the AddIn service)
201 
202     return OUString();
203 }
204 
getDisplayFunctionName(const OUString & aProgrammaticName)205 OUString SAL_CALL AnalysisAddIn::getDisplayFunctionName( const OUString& aProgrammaticName )
206 {
207     OUString          aRet;
208 
209     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) );
210     if( it != pFD->end() )
211     {
212         aRet = AnalysisResId(it->GetUINameID());
213         if( it->IsDouble() )
214         {
215             const OUString& rSuffix = it->GetSuffix();
216             if (!rSuffix.isEmpty())
217                 aRet += rSuffix;
218             else
219                 aRet += "_ADD";
220         }
221     }
222     else
223     {
224         aRet = "UNKNOWNFUNC_" + aProgrammaticName;
225     }
226 
227     return aRet;
228 }
229 
getFunctionDescription(const OUString & aProgrammaticName)230 OUString SAL_CALL AnalysisAddIn::getFunctionDescription( const OUString& aProgrammaticName )
231 {
232     OUString          aRet;
233 
234     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) );
235     if( it != pFD->end() )
236         aRet = GetFuncDescrStr( it->GetDescrID(), 1 );
237 
238     return aRet;
239 }
240 
getDisplayArgumentName(const OUString & aName,sal_Int32 nArg)241 OUString SAL_CALL AnalysisAddIn::getDisplayArgumentName( const OUString& aName, sal_Int32 nArg )
242 {
243     OUString          aRet;
244 
245     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) );
246     if( it != pFD->end() && nArg <= 0xFFFF )
247     {
248         sal_uInt16  nStr = it->GetStrIndex( sal_uInt16( nArg ) );
249         if( nStr )
250             aRet = GetFuncDescrStr( it->GetDescrID(), nStr );
251         else
252             aRet = "internal";
253     }
254 
255     return aRet;
256 }
257 
getArgumentDescription(const OUString & aName,sal_Int32 nArg)258 OUString SAL_CALL AnalysisAddIn::getArgumentDescription( const OUString& aName, sal_Int32 nArg )
259 {
260     OUString          aRet;
261 
262     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) );
263     if( it != pFD->end() && nArg <= 0xFFFF )
264     {
265         sal_uInt16  nStr = it->GetStrIndex( sal_uInt16( nArg ) );
266         if( nStr )
267             aRet = GetFuncDescrStr( it->GetDescrID(), nStr + 1 );
268         else
269             aRet = "for internal use only";
270     }
271 
272     return aRet;
273 }
274 
275 static const char pDefCatName[] = "Add-In";
276 
getProgrammaticCategoryName(const OUString & aName)277 OUString SAL_CALL AnalysisAddIn::getProgrammaticCategoryName( const OUString& aName )
278 {
279     //  return non-translated strings
280     //  return OUString( "Add-In" );
281     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aName ) );
282     OUString              aRet;
283     if( it != pFD->end() )
284     {
285         switch( it->GetCategory() )
286         {
287             case FDCategory::DateTime:    aRet = "Date&Time";         break;
288             case FDCategory::Finance:     aRet = "Financial";         break;
289             case FDCategory::Inf:         aRet = "Information";       break;
290             case FDCategory::Math:        aRet = "Mathematical";      break;
291             case FDCategory::Tech:        aRet = "Technical";         break;
292         }
293     }
294     else
295         aRet = pDefCatName;
296 
297     return aRet;
298 }
299 
getDisplayCategoryName(const OUString & aProgrammaticFunctionName)300 OUString SAL_CALL AnalysisAddIn::getDisplayCategoryName( const OUString& aProgrammaticFunctionName )
301 {
302     //  return translated strings, not used for predefined categories
303     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticFunctionName ) );
304     OUString              aRet;
305     if( it != pFD->end() )
306     {
307         switch( it->GetCategory() )
308         {
309             case FDCategory::DateTime:    aRet = "Date&Time";         break;
310             case FDCategory::Finance:     aRet = "Financial";         break;
311             case FDCategory::Inf:         aRet = "Information";       break;
312             case FDCategory::Math:        aRet = "Mathematical";      break;
313             case FDCategory::Tech:        aRet = "Technical";         break;
314         }
315     }
316     else
317         aRet = pDefCatName;
318 
319     return aRet;
320 }
321 
322 static const sal_Char*      pLang[] = { "de", "en" };
323 static const sal_Char*      pCoun[] = { "DE", "US" };
324 static const sal_uInt32     nNumOfLoc = SAL_N_ELEMENTS(pLang);
325 
InitDefLocales()326 void AnalysisAddIn::InitDefLocales()
327 {
328     pDefLocales.reset( new lang::Locale[ nNumOfLoc ] );
329 
330     for( sal_uInt32 n = 0 ; n < nNumOfLoc ; n++ )
331     {
332         pDefLocales[ n ].Language = OUString::createFromAscii( pLang[ n ] );
333         pDefLocales[ n ].Country = OUString::createFromAscii( pCoun[ n ] );
334     }
335 }
336 
GetLocale(sal_uInt32 nInd)337 inline const lang::Locale& AnalysisAddIn::GetLocale( sal_uInt32 nInd )
338 {
339     if( !pDefLocales )
340         InitDefLocales();
341 
342     if( nInd < sizeof( pLang ) )
343         return pDefLocales[ nInd ];
344     else
345         return aFuncLoc;
346 }
347 
getCompatibilityNames(const OUString & aProgrammaticName)348 uno::Sequence< sheet::LocalizedName > SAL_CALL AnalysisAddIn::getCompatibilityNames( const OUString& aProgrammaticName )
349 {
350     auto it = std::find_if(pFD->begin(), pFD->end(), FindFuncData( aProgrammaticName ) );
351     if( it == pFD->end() )
352         return uno::Sequence< sheet::LocalizedName >( 0 );
353 
354     const std::vector<OUString>& r = it->GetCompNameList();
355     sal_uInt32                   nCount = r.size();
356 
357     uno::Sequence< sheet::LocalizedName >                aRet( nCount );
358 
359     sheet::LocalizedName*  pArray = aRet.getArray();
360 
361     for( sal_uInt32 n = 0 ; n < nCount ; n++ )
362     {
363         pArray[ n ] = sheet::LocalizedName( GetLocale( n ), r[n] );
364     }
365 
366     return aRet;
367 }
368 
369 // XAnalysis
370 /** Workday */
getWorkday(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate,sal_Int32 nDays,const uno::Any & aHDay)371 sal_Int32 SAL_CALL AnalysisAddIn::getWorkday( const uno::Reference< beans::XPropertySet >& xOptions,
372     sal_Int32 nDate, sal_Int32 nDays, const uno::Any& aHDay )
373 {
374     if( !nDays )
375         return nDate;
376 
377     sal_Int32                   nNullDate = GetNullDate( xOptions );
378 
379     SortedIndividualInt32List   aSrtLst;
380 
381     aSrtLst.InsertHolidayList( aAnyConv, xOptions, aHDay, nNullDate );
382 
383     sal_Int32                   nActDate = nDate + nNullDate;
384 
385     if( nDays > 0 )
386     {
387         if( GetDayOfWeek( nActDate ) == 5 )
388             // when starting on Saturday, assuming we're starting on Sunday to get the jump over the weekend
389             nActDate++;
390 
391         while( nDays )
392         {
393             nActDate++;
394 
395             if( GetDayOfWeek( nActDate ) < 5 )
396             {
397                 if( !aSrtLst.Find( nActDate ) )
398                     nDays--;
399             }
400             else
401                 nActDate++;     // jump over weekend
402         }
403     }
404     else
405     {
406         if( GetDayOfWeek( nActDate ) == 6 )
407             // when starting on Sunday, assuming we're starting on Saturday to get the jump over the weekend
408             nActDate--;
409 
410         while( nDays )
411         {
412             nActDate--;
413 
414             if( GetDayOfWeek( nActDate ) < 5 )
415             {
416                 if( !aSrtLst.Find( nActDate ) )
417                     nDays++;
418             }
419             else
420                 nActDate--;     // jump over weekend
421         }
422     }
423 
424     return nActDate - nNullDate;
425 }
426 
427 /** Yearfrac */
getYearfrac(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nStartDate,sal_Int32 nEndDate,const uno::Any & rMode)428 double SAL_CALL AnalysisAddIn::getYearfrac( const uno::Reference< beans::XPropertySet >& xOpt,
429     sal_Int32 nStartDate, sal_Int32 nEndDate, const uno::Any& rMode )
430 {
431     double fRet = GetYearFrac( xOpt, nStartDate, nEndDate, getDateMode( xOpt, rMode ) );
432     RETURN_FINITE( fRet );
433 }
434 
getEdate(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nStartDate,sal_Int32 nMonths)435 sal_Int32 SAL_CALL AnalysisAddIn::getEdate( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nStartDate, sal_Int32 nMonths )
436 {
437     sal_Int32 nNullDate = GetNullDate( xOpt );
438     ScaDate aDate( nNullDate, nStartDate, 5 );
439     aDate.addMonths( nMonths );
440     return aDate.getDate( nNullDate );
441 }
442 
getWeeknum(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nDate,sal_Int32 nMode)443 sal_Int32 SAL_CALL AnalysisAddIn::getWeeknum( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nDate, sal_Int32 nMode )
444 {
445     nDate += GetNullDate( xOpt );
446 
447     sal_uInt16  nDay, nMonth, nYear;
448     DaysToDate( nDate, nDay, nMonth, nYear );
449 
450     sal_Int32   nFirstInYear = DateToDays( 1, 1, nYear );
451     sal_uInt16  nFirstDayInYear = GetDayOfWeek( nFirstInYear );
452 
453     return ( nDate - nFirstInYear + ( ( nMode == 1 )? ( nFirstDayInYear + 1 ) % 7 : nFirstDayInYear ) ) / 7 + 1;
454 }
455 
getEomonth(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nDate,sal_Int32 nMonths)456 sal_Int32 SAL_CALL AnalysisAddIn::getEomonth( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nDate, sal_Int32 nMonths )
457 {
458     sal_Int32   nNullDate = GetNullDate( xOpt );
459     nDate += nNullDate;
460     sal_uInt16  nDay, nMonth, nYear;
461     DaysToDate( nDate, nDay, nMonth, nYear );
462 
463     sal_Int32   nNewMonth = nMonth + nMonths;
464 
465     if( nNewMonth > 12 )
466     {
467         nYear = sal::static_int_cast<sal_uInt16>( nYear + ( nNewMonth / 12 ) );
468         nNewMonth %= 12;
469     }
470     else if( nNewMonth < 1 )
471     {
472         nNewMonth = -nNewMonth;
473         nYear = sal::static_int_cast<sal_uInt16>( nYear - ( nNewMonth / 12 ) );
474         nYear--;
475         nNewMonth %= 12;
476         nNewMonth = 12 - nNewMonth;
477     }
478 
479     return DateToDays( DaysInMonth( sal_uInt16( nNewMonth ), nYear ), sal_uInt16( nNewMonth ), nYear ) - nNullDate;
480 }
481 
getNetworkdays(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nStartDate,sal_Int32 nEndDate,const uno::Any & aHDay)482 sal_Int32 SAL_CALL AnalysisAddIn::getNetworkdays( const uno::Reference< beans::XPropertySet >& xOpt,
483         sal_Int32 nStartDate, sal_Int32 nEndDate, const uno::Any& aHDay )
484 {
485     sal_Int32                   nNullDate = GetNullDate( xOpt );
486 
487     SortedIndividualInt32List   aSrtLst;
488 
489     aSrtLst.InsertHolidayList( aAnyConv, xOpt, aHDay, nNullDate );
490 
491     sal_Int32                   nActDate = nStartDate + nNullDate;
492     sal_Int32                   nStopDate = nEndDate + nNullDate;
493     sal_Int32                   nCnt = 0;
494 
495     if( nActDate <= nStopDate )
496     {
497         while( nActDate <= nStopDate )
498         {
499             if( GetDayOfWeek( nActDate ) < 5 && !aSrtLst.Find( nActDate ) )
500                 nCnt++;
501 
502             nActDate++;
503         }
504     }
505     else
506     {
507         while( nActDate >= nStopDate )
508         {
509             if( GetDayOfWeek( nActDate ) < 5 && !aSrtLst.Find( nActDate ) )
510                 nCnt--;
511 
512             nActDate--;
513         }
514     }
515 
516     return nCnt;
517 }
518 
getIseven(sal_Int32 nVal)519 sal_Int32 SAL_CALL AnalysisAddIn::getIseven( sal_Int32 nVal )
520 {
521     return ( nVal & 0x00000001 )? 0 : 1;
522 }
523 
getIsodd(sal_Int32 nVal)524 sal_Int32 SAL_CALL AnalysisAddIn::getIsodd( sal_Int32 nVal )
525 {
526     return ( nVal & 0x00000001 )? 1 : 0;
527 }
528 
529 double SAL_CALL
getMultinomial(const uno::Reference<beans::XPropertySet> & xOpt,const uno::Sequence<uno::Sequence<sal_Int32>> & aVLst,const uno::Sequence<uno::Any> & aOptVLst)530 AnalysisAddIn::getMultinomial( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< sal_Int32 > >& aVLst,
531                                const uno::Sequence< uno::Any >& aOptVLst )
532 {
533     ScaDoubleListGE0 aValList;
534 
535     aValList.Append( aVLst );
536     aValList.Append( aAnyConv, xOpt, aOptVLst );
537 
538     if( aValList.Count() == 0 )
539         return 0.0;
540 
541     double nZ = 0;
542     double fRet = 1.0;
543 
544     for( sal_uInt32 i = 0; i < aValList.Count(); ++i )
545     {
546         const double d = aValList.Get(i);
547         double n = (d >= 0.0) ? rtl::math::approxFloor( d ) : rtl::math::approxCeil( d );
548         if ( n < 0.0 )
549             throw lang::IllegalArgumentException();
550 
551         if( n > 0.0 )
552         {
553             nZ += n;
554             fRet *= BinomialCoefficient(nZ, n);
555         }
556     }
557     RETURN_FINITE( fRet );
558 }
559 
getSeriessum(double fX,double fN,double fM,const uno::Sequence<uno::Sequence<double>> & aCoeffList)560 double SAL_CALL AnalysisAddIn::getSeriessum( double fX, double fN, double fM, const uno::Sequence< uno::Sequence< double > >& aCoeffList )
561 {
562     double                          fRet = 0.0;
563 
564     // #i32269# 0^0 is undefined, Excel returns #NUM! error
565     if( fX == 0.0 && fN == 0 )
566         throw uno::RuntimeException();
567 
568     if( fX != 0.0 )
569     {
570         for( const uno::Sequence< double >& rList : aCoeffList )
571         {
572             for( const double fCoef : rList )
573             {
574                 fRet += fCoef * pow( fX, fN );
575 
576                 fN += fM;
577             }
578         }
579     }
580 
581     RETURN_FINITE( fRet );
582 }
583 
getQuotient(double fNum,double fDenom)584 double SAL_CALL AnalysisAddIn::getQuotient( double fNum, double fDenom )
585 {
586     double fRet;
587     if( (fNum < 0) != (fDenom < 0) )
588         fRet = ::rtl::math::approxCeil( fNum / fDenom );
589     else
590         fRet = ::rtl::math::approxFloor( fNum / fDenom );
591     RETURN_FINITE( fRet );
592 }
593 
getMround(double fNum,double fMult)594 double SAL_CALL AnalysisAddIn::getMround( double fNum, double fMult )
595 {
596     if( fMult == 0.0 )
597         return fMult;
598 
599     double fRet = fMult * ::rtl::math::round( fNum / fMult );
600     RETURN_FINITE( fRet );
601 }
602 
getSqrtpi(double fNum)603 double SAL_CALL AnalysisAddIn::getSqrtpi( double fNum )
604 {
605     double fRet = sqrt( fNum * PI );
606     RETURN_FINITE( fRet );
607 }
608 
getRandbetween(double fMin,double fMax)609 double SAL_CALL AnalysisAddIn::getRandbetween( double fMin, double fMax )
610 {
611     fMin = ::rtl::math::round( fMin, 0, rtl_math_RoundingMode_Up );
612     fMax = ::rtl::math::round( fMax, 0, rtl_math_RoundingMode_Up );
613     if( fMin > fMax )
614         throw lang::IllegalArgumentException();
615 
616     double fRet = floor(comphelper::rng::uniform_real_distribution(fMin, nextafter(fMax+1, -DBL_MAX)));
617     RETURN_FINITE( fRet );
618 }
619 
getGcd(const uno::Reference<beans::XPropertySet> & xOpt,const uno::Sequence<uno::Sequence<double>> & aVLst,const uno::Sequence<uno::Any> & aOptVLst)620 double SAL_CALL AnalysisAddIn::getGcd( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< double > >& aVLst, const uno::Sequence< uno::Any >& aOptVLst )
621 {
622     ScaDoubleListGT0 aValList;
623 
624     aValList.Append( aVLst );
625     aValList.Append( aAnyConv, xOpt, aOptVLst );
626 
627     if( aValList.Count() == 0 )
628         return 0.0;
629 
630     double          f = aValList.Get(0);
631     for( sal_uInt32 i = 1; i < aValList.Count(); ++i )
632     {
633         f = GetGcd( aValList.Get(i), f );
634     }
635 
636     RETURN_FINITE( f );
637 }
638 
getLcm(const uno::Reference<beans::XPropertySet> & xOpt,const uno::Sequence<uno::Sequence<double>> & aVLst,const uno::Sequence<uno::Any> & aOptVLst)639 double SAL_CALL AnalysisAddIn::getLcm( const uno::Reference< beans::XPropertySet >& xOpt, const uno::Sequence< uno::Sequence< double > >& aVLst, const uno::Sequence< uno::Any >& aOptVLst )
640 {
641     ScaDoubleListGE0 aValList;
642 
643     aValList.Append( aVLst );
644     aValList.Append( aAnyConv, xOpt, aOptVLst );
645 
646     if( aValList.Count() == 0 )
647         return 0.0;
648 
649     double f = rtl::math::approxFloor( aValList.Get(0) );
650     if( f < 0.0 )
651         throw lang::IllegalArgumentException();
652 
653     if( f == 0.0 )
654         return f;
655 
656     for( sal_uInt32 i = 1; i < aValList.Count(); ++i )
657     {
658         double fTmp = rtl::math::approxFloor( aValList.Get(i) );
659         if( fTmp < 0.0 )
660             throw lang::IllegalArgumentException();
661 
662         f = fTmp * f / GetGcd( fTmp, f );
663         if( f == 0.0 )
664             return f;
665     }
666 
667     RETURN_FINITE( f );
668 }
669 
getBesseli(double fNum,sal_Int32 nOrder)670 double SAL_CALL AnalysisAddIn::getBesseli( double fNum, sal_Int32 nOrder )
671 {
672     double fRet = sca::analysis::BesselI( fNum, nOrder );
673     RETURN_FINITE( fRet );
674 }
675 
getBesselj(double fNum,sal_Int32 nOrder)676 double SAL_CALL AnalysisAddIn::getBesselj( double fNum, sal_Int32 nOrder )
677 {
678     double fRet = sca::analysis::BesselJ( fNum, nOrder );
679     RETURN_FINITE( fRet );
680 }
681 
getBesselk(double fNum,sal_Int32 nOrder)682 double SAL_CALL AnalysisAddIn::getBesselk( double fNum, sal_Int32 nOrder )
683 {
684     if( nOrder < 0 || fNum <= 0.0 )
685         throw lang::IllegalArgumentException();
686 
687     double fRet = sca::analysis::BesselK( fNum, nOrder );
688     RETURN_FINITE( fRet );
689 }
690 
getBessely(double fNum,sal_Int32 nOrder)691 double SAL_CALL AnalysisAddIn::getBessely( double fNum, sal_Int32 nOrder )
692 {
693     if( nOrder < 0 || fNum <= 0.0 )
694         throw lang::IllegalArgumentException();
695 
696     double fRet = sca::analysis::BesselY( fNum, nOrder );
697     RETURN_FINITE( fRet );
698 }
699 
700 const double    SCA_MAX2        = 511.0;            // min. val for binary numbers (9 bits + sign)
701 const double    SCA_MIN2        = -SCA_MAX2-1.0;    // min. val for binary numbers (9 bits + sign)
702 const double    SCA_MAX8        = 536870911.0;      // max. val for octal numbers (29 bits + sign)
703 const double    SCA_MIN8        = -SCA_MAX8-1.0;    // min. val for octal numbers (29 bits + sign)
704 const double    SCA_MAX16       = 549755813888.0;   // max. val for hexadecimal numbers (39 bits + sign)
705 const double    SCA_MIN16       = -SCA_MAX16-1.0;   // min. val for hexadecimal numbers (39 bits + sign)
706 const sal_Int32 SCA_MAXPLACES   = 10;               // max. number of places
707 
getBin2Oct(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)708 OUString SAL_CALL AnalysisAddIn::getBin2Oct( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
709 {
710     double fVal = ConvertToDec( aNum, 2, SCA_MAXPLACES );
711     sal_Int32 nPlaces = 0;
712     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
713     return ConvertFromDec( fVal, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces );
714 }
715 
getBin2Dec(const OUString & aNum)716 double SAL_CALL AnalysisAddIn::getBin2Dec( const OUString& aNum )
717 {
718     double fRet = ConvertToDec( aNum, 2, SCA_MAXPLACES );
719     RETURN_FINITE( fRet );
720 }
721 
getBin2Hex(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)722 OUString SAL_CALL AnalysisAddIn::getBin2Hex( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
723 {
724     double fVal = ConvertToDec( aNum, 2, SCA_MAXPLACES );
725     sal_Int32 nPlaces = 0;
726     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
727     return ConvertFromDec( fVal, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces );
728 }
729 
getOct2Bin(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)730 OUString SAL_CALL AnalysisAddIn::getOct2Bin( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
731 {
732     double fVal = ConvertToDec( aNum, 8, SCA_MAXPLACES );
733     sal_Int32 nPlaces = 0;
734     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
735     return ConvertFromDec( fVal, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces );
736 }
737 
getOct2Dec(const OUString & aNum)738 double SAL_CALL AnalysisAddIn::getOct2Dec( const OUString& aNum )
739 {
740     double fRet = ConvertToDec( aNum, 8, SCA_MAXPLACES );
741     RETURN_FINITE( fRet );
742 }
743 
getOct2Hex(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)744 OUString SAL_CALL AnalysisAddIn::getOct2Hex( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
745 {
746     double fVal = ConvertToDec( aNum, 8, SCA_MAXPLACES );
747     sal_Int32 nPlaces = 0;
748     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
749     return ConvertFromDec( fVal, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces );
750 }
751 
getDec2Bin(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nNum,const uno::Any & rPlaces)752 OUString SAL_CALL AnalysisAddIn::getDec2Bin( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nNum, const uno::Any& rPlaces )
753 {
754     sal_Int32 nPlaces = 0;
755     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
756     return ConvertFromDec( nNum, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces );
757 }
758 
getDec2Oct(const uno::Reference<beans::XPropertySet> & xOpt,sal_Int32 nNum,const uno::Any & rPlaces)759 OUString SAL_CALL AnalysisAddIn::getDec2Oct( const uno::Reference< beans::XPropertySet >& xOpt, sal_Int32 nNum, const uno::Any& rPlaces )
760 {
761     sal_Int32 nPlaces = 0;
762     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
763     return ConvertFromDec( nNum, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces );
764 }
765 
getDec2Hex(const uno::Reference<beans::XPropertySet> & xOpt,double fNum,const uno::Any & rPlaces)766 OUString SAL_CALL AnalysisAddIn::getDec2Hex( const uno::Reference< beans::XPropertySet >& xOpt, double fNum, const uno::Any& rPlaces )
767 {
768     sal_Int32 nPlaces = 0;
769     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
770     return ConvertFromDec( fNum, SCA_MIN16, SCA_MAX16, 16, nPlaces, SCA_MAXPLACES, bUsePlaces );
771 }
772 
getHex2Bin(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)773 OUString SAL_CALL AnalysisAddIn::getHex2Bin( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
774 {
775     double fVal = ConvertToDec( aNum, 16, SCA_MAXPLACES );
776     sal_Int32 nPlaces = 0;
777     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
778     return ConvertFromDec( fVal, SCA_MIN2, SCA_MAX2, 2, nPlaces, SCA_MAXPLACES, bUsePlaces );
779 }
780 
getHex2Dec(const OUString & aNum)781 double SAL_CALL AnalysisAddIn::getHex2Dec( const OUString& aNum )
782 {
783     double fRet = ConvertToDec( aNum, 16, SCA_MAXPLACES );
784     RETURN_FINITE( fRet );
785 }
786 
getHex2Oct(const uno::Reference<beans::XPropertySet> & xOpt,const OUString & aNum,const uno::Any & rPlaces)787 OUString SAL_CALL AnalysisAddIn::getHex2Oct( const uno::Reference< beans::XPropertySet >& xOpt, const OUString& aNum, const uno::Any& rPlaces )
788 {
789     double fVal = ConvertToDec( aNum, 16, SCA_MAXPLACES );
790     sal_Int32 nPlaces = 0;
791     bool bUsePlaces = aAnyConv.getInt32( nPlaces, xOpt, rPlaces );
792     return ConvertFromDec( fVal, SCA_MIN8, SCA_MAX8, 8, nPlaces, SCA_MAXPLACES, bUsePlaces );
793 }
794 
getDelta(const uno::Reference<beans::XPropertySet> & xOpt,double fNum1,const uno::Any & rNum2)795 sal_Int32 SAL_CALL AnalysisAddIn::getDelta( const uno::Reference< beans::XPropertySet >& xOpt, double fNum1, const uno::Any& rNum2 )
796 {
797     return sal_Int32(fNum1 == aAnyConv.getDouble( xOpt, rNum2, 0.0 ));
798 }
799 
getErf(const uno::Reference<beans::XPropertySet> & xOpt,double fLL,const uno::Any & rUL)800 double SAL_CALL AnalysisAddIn::getErf( const uno::Reference< beans::XPropertySet >& xOpt, double fLL, const uno::Any& rUL )
801 {
802     double fUL, fRet;
803     bool bContainsValue = aAnyConv.getDouble( fUL, xOpt, rUL );
804 
805     fRet = bContainsValue ? (Erf( fUL ) - Erf( fLL )) : Erf( fLL );
806     RETURN_FINITE( fRet );
807 }
808 
getErfc(double f)809 double SAL_CALL AnalysisAddIn::getErfc( double f )
810 {
811     double fRet = Erfc( f );
812     RETURN_FINITE( fRet );
813 }
814 
getGestep(const uno::Reference<beans::XPropertySet> & xOpt,double fNum,const uno::Any & rStep)815 sal_Int32 SAL_CALL AnalysisAddIn::getGestep( const uno::Reference< beans::XPropertySet >& xOpt, double fNum, const uno::Any& rStep )
816 {
817     return sal_Int32(fNum >= aAnyConv.getDouble( xOpt, rStep, 0.0 ));
818 }
819 
getFactdouble(sal_Int32 nNum)820 double SAL_CALL AnalysisAddIn::getFactdouble( sal_Int32 nNum )
821 {
822     double fRet = FactDouble( nNum );
823     RETURN_FINITE( fRet );
824 }
825 
getImabs(const OUString & aNum)826 double SAL_CALL AnalysisAddIn::getImabs( const OUString& aNum )
827 {
828     double fRet = Complex( aNum ).Abs();
829     RETURN_FINITE( fRet );
830 }
831 
getImaginary(const OUString & aNum)832 double SAL_CALL AnalysisAddIn::getImaginary( const OUString& aNum )
833 {
834     double fRet = Complex( aNum ).Imag();
835     RETURN_FINITE( fRet );
836 }
837 
getImpower(const OUString & aNum,double f)838 OUString SAL_CALL AnalysisAddIn::getImpower( const OUString& aNum, double f )
839 {
840     Complex     z( aNum );
841 
842     z.Power( f );
843 
844     return z.GetString();
845 }
846 
getImargument(const OUString & aNum)847 double SAL_CALL AnalysisAddIn::getImargument( const OUString& aNum )
848 {
849     double fRet = Complex( aNum ).Arg();
850     RETURN_FINITE( fRet );
851 }
852 
getImcos(const OUString & aNum)853 OUString SAL_CALL AnalysisAddIn::getImcos( const OUString& aNum )
854 {
855     Complex     z( aNum );
856 
857     z.Cos();
858 
859     return z.GetString();
860 }
861 
getImdiv(const OUString & aDivid,const OUString & aDivis)862 OUString SAL_CALL AnalysisAddIn::getImdiv( const OUString& aDivid, const OUString& aDivis )
863 {
864     Complex     z( aDivid );
865 
866     z.Div( Complex( aDivis ) );
867 
868     return z.GetString();
869 }
870 
getImexp(const OUString & aNum)871 OUString SAL_CALL AnalysisAddIn::getImexp( const OUString& aNum )
872 {
873     Complex     z( aNum );
874 
875     z.Exp();
876 
877     return z.GetString();
878 }
879 
getImconjugate(const OUString & aNum)880 OUString SAL_CALL AnalysisAddIn::getImconjugate( const OUString& aNum )
881 {
882     Complex     z( aNum );
883 
884     z.Conjugate();
885 
886     return z.GetString();
887 }
888 
getImln(const OUString & aNum)889 OUString SAL_CALL AnalysisAddIn::getImln( const OUString& aNum )
890 {
891     Complex     z( aNum );
892 
893     z.Ln();
894 
895     return z.GetString();
896 }
897 
getImlog10(const OUString & aNum)898 OUString SAL_CALL AnalysisAddIn::getImlog10( const OUString& aNum )
899 {
900     Complex     z( aNum );
901 
902     z.Log10();
903 
904     return z.GetString();
905 }
906 
getImlog2(const OUString & aNum)907 OUString SAL_CALL AnalysisAddIn::getImlog2( const OUString& aNum )
908 {
909     Complex     z( aNum );
910 
911     z.Log2();
912 
913     return z.GetString();
914 }
915 
getImproduct(const uno::Reference<beans::XPropertySet> &,const uno::Sequence<uno::Sequence<OUString>> & aNum1,const uno::Sequence<uno::Any> & aNL)916 OUString SAL_CALL AnalysisAddIn::getImproduct( const uno::Reference< beans::XPropertySet >&, const uno::Sequence< uno::Sequence< OUString > >& aNum1, const uno::Sequence< uno::Any >& aNL )
917 {
918     ComplexList     z_list;
919 
920     z_list.Append( aNum1 );
921     z_list.Append( aNL );
922 
923     if( z_list.empty() )
924         return Complex( 0 ).GetString();
925 
926     Complex         z = z_list.Get(0);
927     for( sal_uInt32 i = 1; i < z_list.Count(); ++i )
928         z.Mult( z_list.Get(i) );
929 
930     return z.GetString();
931 }
932 
getImreal(const OUString & aNum)933 double SAL_CALL AnalysisAddIn::getImreal( const OUString& aNum )
934 {
935     double fRet = Complex( aNum ).Real();
936     RETURN_FINITE( fRet );
937 }
938 
getImsin(const OUString & aNum)939 OUString SAL_CALL AnalysisAddIn::getImsin( const OUString& aNum )
940 {
941     Complex     z( aNum );
942 
943     z.Sin();
944 
945     return z.GetString();
946 }
947 
getImsub(const OUString & aNum1,const OUString & aNum2)948 OUString SAL_CALL AnalysisAddIn::getImsub( const OUString& aNum1, const OUString& aNum2 )
949 {
950     Complex     z( aNum1 );
951 
952     z.Sub( Complex( aNum2 ) );
953 
954     return z.GetString();
955 }
956 
getImsum(const uno::Reference<beans::XPropertySet> &,const uno::Sequence<uno::Sequence<OUString>> & aNum1,const uno::Sequence<uno::Any> & aFollowingPars)957 OUString SAL_CALL AnalysisAddIn::getImsum( const uno::Reference< beans::XPropertySet >&, const uno::Sequence< uno::Sequence< OUString > >& aNum1, const uno::Sequence< uno::Any >& aFollowingPars )
958 {
959     ComplexList     z_list;
960 
961     z_list.Append( aNum1 );
962     z_list.Append( aFollowingPars );
963 
964     if( z_list.empty() )
965         return Complex( 0 ).GetString();
966 
967     Complex         z( z_list.Get(0) );
968     for( sal_uInt32 i = 1; i < z_list.Count(); ++i )
969         z.Add( z_list.Get(i) );
970 
971     return z.GetString();
972 }
973 
getImsqrt(const OUString & aNum)974 OUString SAL_CALL AnalysisAddIn::getImsqrt( const OUString& aNum )
975 {
976     Complex     z( aNum );
977 
978     z.Sqrt();
979 
980     return z.GetString();
981 }
982 
getImtan(const OUString & aNum)983 OUString SAL_CALL AnalysisAddIn::getImtan( const OUString& aNum )
984 {
985     Complex     z( aNum );
986 
987     z.Tan();
988 
989     return z.GetString();
990 }
991 
getImsec(const OUString & aNum)992 OUString SAL_CALL AnalysisAddIn::getImsec( const OUString& aNum )
993 {
994     Complex     z( aNum );
995 
996     z.Sec();
997 
998     return z.GetString();
999 }
1000 
getImcsc(const OUString & aNum)1001 OUString SAL_CALL AnalysisAddIn::getImcsc( const OUString& aNum )
1002 {
1003     Complex     z( aNum );
1004 
1005     z.Csc();
1006 
1007     return z.GetString();
1008 }
1009 
getImcot(const OUString & aNum)1010 OUString SAL_CALL AnalysisAddIn::getImcot( const OUString& aNum )
1011 {
1012     Complex     z( aNum );
1013 
1014     z.Cot();
1015 
1016     return z.GetString();
1017 }
1018 
getImsinh(const OUString & aNum)1019 OUString SAL_CALL AnalysisAddIn::getImsinh( const OUString& aNum )
1020 {
1021     Complex     z( aNum );
1022 
1023     z.Sinh();
1024 
1025     return z.GetString();
1026 }
1027 
getImcosh(const OUString & aNum)1028 OUString SAL_CALL AnalysisAddIn::getImcosh( const OUString& aNum )
1029 {
1030     Complex     z( aNum );
1031 
1032     z.Cosh();
1033 
1034     return z.GetString();
1035 }
1036 
getImsech(const OUString & aNum)1037 OUString SAL_CALL AnalysisAddIn::getImsech( const OUString& aNum )
1038 {
1039     Complex     z( aNum );
1040 
1041     z.Sech();
1042 
1043     return z.GetString();
1044 }
1045 
getImcsch(const OUString & aNum)1046 OUString SAL_CALL AnalysisAddIn::getImcsch( const OUString& aNum )
1047 {
1048     Complex     z( aNum );
1049 
1050     z.Csch();
1051 
1052     return z.GetString();
1053 }
1054 
getComplex(double fR,double fI,const uno::Any & rSuff)1055 OUString SAL_CALL AnalysisAddIn::getComplex( double fR, double fI, const uno::Any& rSuff )
1056 {
1057     bool    bi;
1058 
1059     switch( rSuff.getValueTypeClass() )
1060     {
1061         case uno::TypeClass_VOID:
1062             bi = true;
1063             break;
1064         case uno::TypeClass_STRING:
1065             {
1066             auto   pSuff = o3tl::forceAccess<OUString>(rSuff);
1067             bi = *pSuff == "i" || pSuff->isEmpty();
1068             if( !bi && *pSuff != "j" )
1069                 throw lang::IllegalArgumentException();
1070             }
1071             break;
1072         default:
1073             throw lang::IllegalArgumentException();
1074     }
1075 
1076     return Complex( fR, fI, bi ? 'i' : 'j' ).GetString();
1077 }
1078 
getConvert(double f,const OUString & aFU,const OUString & aTU)1079 double SAL_CALL AnalysisAddIn::getConvert( double f, const OUString& aFU, const OUString& aTU )
1080 {
1081     if( !pCDL )
1082         pCDL.reset(new ConvertDataList());
1083 
1084     double fRet = pCDL->Convert( f, aFU, aTU );
1085     RETURN_FINITE( fRet );
1086 }
1087 
AnalysisResId(const char * pResId)1088 OUString AnalysisAddIn::AnalysisResId(const char* pResId)
1089 {
1090     return Translate::get(pResId, aResLocale);
1091 }
1092 
1093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1094