| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| // INCLUDE --------------------------------------------------------------- |
| |
| #include <tools/solar.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <rtl/logfile.hxx> |
| |
| #include "interpre.hxx" |
| #include "global.hxx" |
| #include "compiler.hxx" |
| #include "cell.hxx" |
| #include "document.hxx" |
| #include "dociter.hxx" |
| #include "scmatrix.hxx" |
| #include "globstr.hrc" |
| |
| #include <math.h> |
| #include <vector> |
| #include <algorithm> |
| |
| #include <boost/math/special_functions/atanh.hpp> |
| #include <boost/math/special_functions/expm1.hpp> |
| #include <boost/math/special_functions/log1p.hpp> |
| |
| using ::std::vector; |
| using namespace formula; |
| |
| // STATIC DATA ----------------------------------------------------------- |
| |
| #define SCdEpsilon 1.0E-7 |
| #define SC_MAX_ITERATION_COUNT 20 |
| #define MAX_ANZ_DOUBLE_FOR_SORT 100000 |
| // PI jetzt als F_PI aus solar.h |
| //#define PI 3.1415926535897932 |
| |
| const double ScInterpreter::fMaxGammaArgument = 171.624376956302; // found experimental |
| const double fMachEps = ::std::numeric_limits<double>::epsilon(); |
| |
| //----------------------------------------------------------------------------- |
| |
| class ScDistFunc |
| { |
| public: |
| virtual double GetValue(double x) const = 0; |
| }; |
| |
| // iteration for inverse distributions |
| |
| //template< class T > double lcl_IterateInverse( const T& rFunction, double x0, double x1, bool& rConvError ) |
| |
| /** u*w<0.0 fails for values near zero */ |
| inline bool lcl_HasChangeOfSign( double u, double w ) |
| { |
| return (u < 0.0 && w > 0.0) || (u > 0.0 && w < 0.0); |
| } |
| |
| double lcl_IterateInverse( const ScDistFunc& rFunction, double fAx, double fBx, bool& rConvError ) |
| { |
| rConvError = false; |
| const double fYEps = 1.0E-307; |
| const double fXEps = ::std::numeric_limits<double>::epsilon(); |
| |
| DBG_ASSERT(fAx<fBx, "IterateInverse: wrong interval"); |
| |
| // find enclosing interval |
| |
| double fAy = rFunction.GetValue(fAx); |
| double fBy = rFunction.GetValue(fBx); |
| double fTemp; |
| unsigned short nCount; |
| for (nCount = 0; nCount < 1000 && !lcl_HasChangeOfSign(fAy,fBy); nCount++) |
| { |
| if (fabs(fAy) <= fabs(fBy)) |
| { |
| fTemp = fAx; |
| fAx += 2.0 * (fAx - fBx); |
| if (fAx < 0.0) |
| fAx = 0.0; |
| fBx = fTemp; |
| fBy = fAy; |
| fAy = rFunction.GetValue(fAx); |
| } |
| else |
| { |
| fTemp = fBx; |
| fBx += 2.0 * (fBx - fAx); |
| fAx = fTemp; |
| fAy = fBy; |
| fBy = rFunction.GetValue(fBx); |
| } |
| } |
| |
| if (fAy == 0.0) |
| return fAx; |
| if (fBy == 0.0) |
| return fBx; |
| if (!lcl_HasChangeOfSign( fAy, fBy)) |
| { |
| rConvError = true; |
| return 0.0; |
| } |
| // inverse quadric interpolation with additional brackets |
| // set three points |
| double fPx = fAx; |
| double fPy = fAy; |
| double fQx = fBx; |
| double fQy = fBy; |
| double fRx = fAx; |
| double fRy = fAy; |
| double fSx = 0.5 * (fAx + fBx); // potential next point |
| bool bHasToInterpolate = true; |
| nCount = 0; |
| while ( nCount < 500 && fabs(fRy) > fYEps && |
| (fBx-fAx) > ::std::max( fabs(fAx), fabs(fBx)) * fXEps ) |
| { |
| if (bHasToInterpolate) |
| { |
| if (fPy!=fQy && fQy!=fRy && fRy!=fPy) |
| { |
| fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy) |
| + fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy) |
| + fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy); |
| bHasToInterpolate = (fAx < fSx) && (fSx < fBx); // inside the brackets? |
| } |
| else |
| bHasToInterpolate = false; |
| } |
| if(!bHasToInterpolate) |
| { |
| fSx = 0.5 * (fAx + fBx); |
| // reset points |
| fPx = fAx; fPy = fAy; |
| fQx = fBx; fQy = fBy; |
| bHasToInterpolate = true; |
| } |
| // shift points for next interpolation |
| fPx = fQx; fQx = fRx; fRx = fSx; |
| fPy = fQy; fQy = fRy; fRy = rFunction.GetValue(fSx); |
| // update brackets |
| if (lcl_HasChangeOfSign( fAy, fRy)) |
| { |
| fBx = fRx; fBy = fRy; |
| } |
| else |
| { |
| fAx = fRx; fAy = fRy; |
| } |
| // if last interration brought to small advance, then do bisection next |
| // time, for safety |
| bHasToInterpolate = bHasToInterpolate && (fabs(fRy) * 2.0 <= fabs(fQy)); |
| ++nCount; |
| } |
| return fRx; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Allgemeine Funktionen |
| //----------------------------------------------------------------------------- |
| |
| void ScInterpreter::ScNoName() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNoName" ); |
| PushError(errNoName); |
| } |
| |
| void ScInterpreter::ScBadName() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBadName" ); |
| short nParamCount = GetByte(); |
| while (nParamCount-- > 0) |
| { |
| PopError(); |
| } |
| PushError( errNoName); |
| } |
| |
| double ScInterpreter::phi(double x) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::phi" ); |
| return 0.39894228040143268 * exp(-(x * x) / 2.0); |
| } |
| |
| double ScInterpreter::integralPhi(double x) |
| { // Using gauss(x)+0.5 has severe cancellation errors for x<-4 |
| return 0.5 * ::rtl::math::erfc(-x * 0.7071067811865475); // * 1/sqrt(2) |
| } |
| |
| double ScInterpreter::taylor(double* pPolynom, sal_uInt16 nMax, double x) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::taylor" ); |
| double nVal = pPolynom[nMax]; |
| for (short i = nMax-1; i >= 0; i--) |
| { |
| nVal = pPolynom[i] + (nVal * x); |
| } |
| return nVal; |
| } |
| |
| double ScInterpreter::gauss(double x) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::gauss" ); |
| double t0[] = |
| { 0.39894228040143268, -0.06649038006690545, 0.00997355701003582, |
| -0.00118732821548045, 0.00011543468761616, -0.00000944465625950, |
| 0.00000066596935163, -0.00000004122667415, 0.00000000227352982, |
| 0.00000000011301172, 0.00000000000511243, -0.00000000000021218 }; |
| double t2[] = |
| { 0.47724986805182079, 0.05399096651318805, -0.05399096651318805, |
| 0.02699548325659403, -0.00449924720943234, -0.00224962360471617, |
| 0.00134977416282970, -0.00011783742691370, -0.00011515930357476, |
| 0.00003704737285544, 0.00000282690796889, -0.00000354513195524, |
| 0.00000037669563126, 0.00000019202407921, -0.00000005226908590, |
| -0.00000000491799345, 0.00000000366377919, -0.00000000015981997, |
| -0.00000000017381238, 0.00000000002624031, 0.00000000000560919, |
| -0.00000000000172127, -0.00000000000008634, 0.00000000000007894 }; |
| double t4[] = |
| { 0.49996832875816688, 0.00013383022576489, -0.00026766045152977, |
| 0.00033457556441221, -0.00028996548915725, 0.00018178605666397, |
| -0.00008252863922168, 0.00002551802519049, -0.00000391665839292, |
| -0.00000074018205222, 0.00000064422023359, -0.00000017370155340, |
| 0.00000000909595465, 0.00000000944943118, -0.00000000329957075, |
| 0.00000000029492075, 0.00000000011874477, -0.00000000004420396, |
| 0.00000000000361422, 0.00000000000143638, -0.00000000000045848 }; |
| double asympt[] = { -1.0, 1.0, -3.0, 15.0, -105.0 }; |
| |
| double xAbs = fabs(x); |
| sal_uInt16 xShort = (sal_uInt16)::rtl::math::approxFloor(xAbs); |
| double nVal = 0.0; |
| if (xShort == 0) |
| nVal = taylor(t0, 11, (xAbs * xAbs)) * xAbs; |
| else if ((xShort >= 1) && (xShort <= 2)) |
| nVal = taylor(t2, 23, (xAbs - 2.0)); |
| else if ((xShort >= 3) && (xShort <= 4)) |
| nVal = taylor(t4, 20, (xAbs - 4.0)); |
| else |
| nVal = 0.5 + phi(xAbs) * taylor(asympt, 4, 1.0 / (xAbs * xAbs)) / xAbs; |
| if (x < 0.0) |
| return -nVal; |
| else |
| return nVal; |
| } |
| |
| // |
| // #i26836# new gaussinv implementation by Martin Eitzenberger <m.eitzenberger@unix.net> |
| // |
| |
| double ScInterpreter::gaussinv(double x) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::gaussinv" ); |
| double q,t,z; |
| |
| q=x-0.5; |
| |
| if(fabs(q)<=.425) |
| { |
| t=0.180625-q*q; |
| |
| z= |
| q* |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*2509.0809287301226727+33430.575583588128105 |
| ) |
| *t+67265.770927008700853 |
| ) |
| *t+45921.953931549871457 |
| ) |
| *t+13731.693765509461125 |
| ) |
| *t+1971.5909503065514427 |
| ) |
| *t+133.14166789178437745 |
| ) |
| *t+3.387132872796366608 |
| ) |
| / |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*5226.495278852854561+28729.085735721942674 |
| ) |
| *t+39307.89580009271061 |
| ) |
| *t+21213.794301586595867 |
| ) |
| *t+5394.1960214247511077 |
| ) |
| *t+687.1870074920579083 |
| ) |
| *t+42.313330701600911252 |
| ) |
| *t+1.0 |
| ); |
| |
| } |
| else |
| { |
| if(q>0) t=1-x; |
| else t=x; |
| |
| t=sqrt(-log(t)); |
| |
| if(t<=5.0) |
| { |
| t+=-1.6; |
| |
| z= |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*7.7454501427834140764e-4+0.0227238449892691845833 |
| ) |
| *t+0.24178072517745061177 |
| ) |
| *t+1.27045825245236838258 |
| ) |
| *t+3.64784832476320460504 |
| ) |
| *t+5.7694972214606914055 |
| ) |
| *t+4.6303378461565452959 |
| ) |
| *t+1.42343711074968357734 |
| ) |
| / |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*1.05075007164441684324e-9+5.475938084995344946e-4 |
| ) |
| *t+0.0151986665636164571966 |
| ) |
| *t+0.14810397642748007459 |
| ) |
| *t+0.68976733498510000455 |
| ) |
| *t+1.6763848301838038494 |
| ) |
| *t+2.05319162663775882187 |
| ) |
| *t+1.0 |
| ); |
| |
| } |
| else |
| { |
| t+=-5.0; |
| |
| z= |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*2.01033439929228813265e-7+2.71155556874348757815e-5 |
| ) |
| *t+0.0012426609473880784386 |
| ) |
| *t+0.026532189526576123093 |
| ) |
| *t+0.29656057182850489123 |
| ) |
| *t+1.7848265399172913358 |
| ) |
| *t+5.4637849111641143699 |
| ) |
| *t+6.6579046435011037772 |
| ) |
| / |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| ( |
| t*2.04426310338993978564e-15+1.4215117583164458887e-7 |
| ) |
| *t+1.8463183175100546818e-5 |
| ) |
| *t+7.868691311456132591e-4 |
| ) |
| *t+0.0148753612908506148525 |
| ) |
| *t+0.13692988092273580531 |
| ) |
| *t+0.59983220655588793769 |
| ) |
| *t+1.0 |
| ); |
| |
| } |
| |
| if(q<0.0) z=-z; |
| } |
| |
| return z; |
| } |
| |
| double ScInterpreter::Fakultaet(double x) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Fakultaet" ); |
| x = ::rtl::math::approxFloor(x); |
| if (x < 0.0) |
| return 0.0; |
| else if (x == 0.0) |
| return 1.0; |
| else if (x <= 170.0) |
| { |
| double fTemp = x; |
| while (fTemp > 2.0) |
| { |
| fTemp--; |
| x *= fTemp; |
| } |
| } |
| else |
| SetError(errNoValue); |
| /* // Stirlingsche Naeherung zu ungenau |
| else |
| x = pow(x/exp(1), x) * sqrt(x) * SQRT_2_PI * (1.0 + 1.0 / (12.0 * x)); |
| */ |
| return x; |
| } |
| |
| double ScInterpreter::BinomKoeff(double n, double k) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::BinomKoeff" ); |
| 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--; |
| } |
| /* |
| double f1 = n; // Zaehler |
| double f2 = k; // Nenner |
| n--; |
| k--; |
| while (k > 0.0) |
| { |
| f2 *= k; |
| f1 *= n; |
| k--; |
| n--; |
| } |
| nVal = f1 / f2; |
| */ |
| } |
| return nVal; |
| } |
| |
| |
| // The algorithm is based on lanczos13m53 in lanczos.hpp |
| // in math library from http://www.boost.org |
| /** you must ensure fZ>0 |
| Uses a variant of the Lanczos sum with a rational function. */ |
| double lcl_getLanczosSum(double fZ) |
| { |
| const double fNum[13] ={ |
| 23531376880.41075968857200767445163675473, |
| 42919803642.64909876895789904700198885093, |
| 35711959237.35566804944018545154716670596, |
| 17921034426.03720969991975575445893111267, |
| 6039542586.35202800506429164430729792107, |
| 1439720407.311721673663223072794912393972, |
| 248874557.8620541565114603864132294232163, |
| 31426415.58540019438061423162831820536287, |
| 2876370.628935372441225409051620849613599, |
| 186056.2653952234950402949897160456992822, |
| 8071.672002365816210638002902272250613822, |
| 210.8242777515793458725097339207133627117, |
| 2.506628274631000270164908177133837338626 |
| }; |
| const double fDenom[13] = { |
| 0, |
| 39916800, |
| 120543840, |
| 150917976, |
| 105258076, |
| 45995730, |
| 13339535, |
| 2637558, |
| 357423, |
| 32670, |
| 1925, |
| 66, |
| 1 |
| }; |
| // Horner scheme |
| double fSumNum; |
| double fSumDenom; |
| int nI; |
| double fZInv; |
| if (fZ<=1.0) |
| { |
| fSumNum = fNum[12]; |
| fSumDenom = fDenom[12]; |
| for (nI = 11; nI >= 0; --nI) |
| { |
| fSumNum *= fZ; |
| fSumNum += fNum[nI]; |
| fSumDenom *= fZ; |
| fSumDenom += fDenom[nI]; |
| } |
| } |
| else |
| // Cancel down with fZ^12; Horner scheme with reverse coefficients |
| { |
| fZInv = 1/fZ; |
| fSumNum = fNum[0]; |
| fSumDenom = fDenom[0]; |
| for (nI = 1; nI <=12; ++nI) |
| { |
| fSumNum *= fZInv; |
| fSumNum += fNum[nI]; |
| fSumDenom *= fZInv; |
| fSumDenom += fDenom[nI]; |
| } |
| } |
| return fSumNum/fSumDenom; |
| } |
| |
| // The algorithm is based on tgamma in gamma.hpp |
| // in math library from http://www.boost.org |
| /** You must ensure fZ>0; fZ>171.624376956302 will overflow. */ |
| double lcl_GetGammaHelper(double fZ) |
| { |
| double fGamma = lcl_getLanczosSum(fZ); |
| const double fg = 6.024680040776729583740234375; |
| double fZgHelp = fZ + fg - 0.5; |
| // avoid intermediate overflow |
| double fHalfpower = pow( fZgHelp, fZ / 2 - 0.25); |
| fGamma *= fHalfpower; |
| fGamma /= exp(fZgHelp); |
| fGamma *= fHalfpower; |
| if (fZ <= 20.0 && fZ == ::rtl::math::approxFloor(fZ)) |
| fGamma = ::rtl::math::round(fGamma); |
| return fGamma; |
| } |
| |
| // The algorithm is based on tgamma in gamma.hpp |
| // in math library from http://www.boost.org |
| /** You must ensure fZ>0 */ |
| double lcl_GetLogGammaHelper(double fZ) |
| { |
| const double fg = 6.024680040776729583740234375; |
| double fZgHelp = fZ + fg - 0.5; |
| return log( lcl_getLanczosSum(fZ)) + (fZ-0.5) * log(fZgHelp) - fZgHelp; |
| } |
| |
| /** You must ensure non integer arguments for fZ<1 */ |
| double ScInterpreter::GetGamma(double fZ) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGamma" ); |
| const double fLogPi = log(F_PI); |
| const double fLogDblMax = log( ::std::numeric_limits<double>::max()); |
| |
| if (fZ > fMaxGammaArgument) |
| { |
| SetError(errIllegalFPOperation); |
| return HUGE_VAL; |
| } |
| |
| if (fZ >= 1.0) |
| return lcl_GetGammaHelper(fZ); |
| |
| if (fZ >= 0.5) // shift to x>=1 using Gamma(x)=Gamma(x+1)/x |
| return lcl_GetGammaHelper(fZ+1) / fZ; |
| |
| if (fZ >= -0.5) // shift to x>=1, might overflow |
| { |
| double fLogTest = lcl_GetLogGammaHelper(fZ+2) - log(fZ+1) - log( fabs(fZ)); |
| if (fLogTest >= fLogDblMax) |
| { |
| SetError( errIllegalFPOperation); |
| return HUGE_VAL; |
| } |
| return lcl_GetGammaHelper(fZ+2) / (fZ+1) / fZ; |
| } |
| // fZ<-0.5 |
| // Use Euler's reflection formula: gamma(x)= pi/ ( gamma(1-x)*sin(pi*x) ) |
| double fLogDivisor = lcl_GetLogGammaHelper(1-fZ) + log( fabs( ::rtl::math::sin( F_PI*fZ))); |
| if (fLogDivisor - fLogPi >= fLogDblMax) // underflow |
| return 0.0; |
| |
| if (fLogDivisor<0.0) |
| if (fLogPi - fLogDivisor > fLogDblMax) // overflow |
| { |
| SetError(errIllegalFPOperation); |
| return HUGE_VAL; |
| } |
| |
| return exp( fLogPi - fLogDivisor) * ((::rtl::math::sin( F_PI*fZ) < 0.0) ? -1.0 : 1.0); |
| } |
| |
| |
| /** You must ensure fZ>0 */ |
| double ScInterpreter::GetLogGamma(double fZ) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetLogGamma" ); |
| if (fZ >= fMaxGammaArgument) |
| return lcl_GetLogGammaHelper(fZ); |
| if (fZ >= 1.0) |
| return log(lcl_GetGammaHelper(fZ)); |
| if (fZ >= 0.5) |
| return log( lcl_GetGammaHelper(fZ+1) / fZ); |
| return lcl_GetLogGammaHelper(fZ+2) - log(fZ+1) - log(fZ); |
| } |
| |
| double ScInterpreter::GetFDist(double x, double fF1, double fF2) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetFDist" ); |
| double arg = fF2/(fF2+fF1*x); |
| double alpha = fF2/2.0; |
| double beta = fF1/2.0; |
| return (GetBetaDist(arg, alpha, beta)); |
| /* |
| double Z = (pow(fF,1.0/3.0)*(1.0-2.0/(9.0*fF2)) - (1.0-2.0/(9.0*fF1))) / |
| sqrt(2.0/(9.0*fF1) + pow(fF,2.0/3.0)*2.0/(9.0*fF2)); |
| return (0.5-gauss(Z)); |
| */ |
| } |
| |
| double ScInterpreter::GetTDist(double T, double fDF) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetTDist" ); |
| return 0.5 * GetBetaDist(fDF/(fDF+T*T), fDF/2.0, 0.5); |
| /* |
| sal_uInt16 DF = (sal_uInt16) fDF; |
| double A = T / sqrt(DF); |
| double B = 1.0 + A*A; |
| double R; |
| if (DF == 1) |
| R = 0.5 + atan(A)/F_PI; |
| else if (DF % 2 == 0) |
| { |
| double S0 = A/(2.0 * sqrt(B)); |
| double C0 = S0; |
| for (sal_uInt16 i = 2; i <= DF-2; i+=2) |
| { |
| C0 *= (1.0 - 1.0/(double)i)/B; |
| S0 += C0; |
| } |
| R = 0.5 + S0; |
| } |
| else |
| { |
| double S1 = A / (B * F_PI); |
| double C1 = S1; |
| for (sal_uInt16 i = 3; i <= DF-2; i+=2) |
| { |
| C1 *= (1.0 - 1.0/(double)i)/B; |
| S1 += C1; |
| } |
| R = 0.5 + atan(A)/F_PI + S1; |
| } |
| return 1.0 - R; |
| */ |
| } |
| |
| // for LEGACY.CHIDIST, returns right tail, fDF=degrees of freedom |
| /** You must ensure fDF>0.0 */ |
| double ScInterpreter::GetChiDist(double fX, double fDF) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetChiDist" ); |
| if (fX <= 0.0) |
| return 1.0; // see ODFF |
| else |
| return GetUpRegIGamma( fDF/2.0, fX/2.0); |
| } |
| |
| // ready for ODF 1.2 |
| // for ODF CHISQDIST; cumulative distribution function, fDF=degrees of freedom |
| // returns left tail |
| /** You must ensure fDF>0.0 */ |
| double ScInterpreter::GetChiSqDistCDF(double fX, double fDF) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetChiSqDistCDF" ); |
| if (fX <= 0.0) |
| return 0.0; // see ODFF |
| else |
| return GetLowRegIGamma( fDF/2.0, fX/2.0); |
| } |
| |
| double ScInterpreter::GetChiSqDistPDF(double fX, double fDF) |
| { |
| // you must ensure fDF is positive integer |
| double fValue; |
| double fCount; |
| if (fX <= 0.0) |
| return 0.0; // see ODFF |
| if (fDF*fX > 1391000.0) |
| { |
| // intermediate invalid values, use log |
| fValue = exp((0.5*fDF - 1) * log(fX*0.5) - 0.5 * fX - log(2.0) - GetLogGamma(0.5*fDF)); |
| } |
| else // fDF is small in most cases, we can iterate |
| { |
| if (fmod(fDF,2.0)<0.5) |
| { |
| // even |
| fValue = 0.5; |
| fCount = 2.0; |
| } |
| else |
| { |
| fValue = 1/sqrt(fX*2*F_PI); |
| fCount = 1.0; |
| } |
| while ( fCount < fDF) |
| { |
| fValue *= (fX / fCount); |
| fCount += 2.0; |
| } |
| if (fX>=1425.0) // underflow in e^(-x/2) |
| fValue = exp(log(fValue)-fX/2); |
| else |
| fValue *= exp(-fX/2); |
| } |
| return fValue; |
| } |
| |
| void ScInterpreter::ScChiSqDist() |
| { |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) |
| return; |
| double fX; |
| bool bCumulative; |
| if (nParamCount == 3) |
| bCumulative = GetBool(); |
| else |
| bCumulative = true; |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| if (fDF < 1.0) |
| PushIllegalArgument(); |
| else |
| { |
| fX = GetDouble(); |
| if (bCumulative) |
| PushDouble(GetChiSqDistCDF(fX,fDF)); |
| else |
| PushDouble(GetChiSqDistPDF(fX,fDF)); |
| } |
| } |
| |
| void ScInterpreter::ScGamma() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGamma" ); |
| double x = GetDouble(); |
| double fResult; |
| if (x <= 0.0 && x == ::rtl::math::approxFloor(x)) |
| PushIllegalArgument(); |
| else |
| { |
| fResult = GetGamma(x); |
| if (nGlobalError) |
| { |
| PushError( nGlobalError); |
| return; |
| } |
| PushDouble(fResult); |
| } |
| } |
| |
| |
| void ScInterpreter::ScLogGamma() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogGamma" ); |
| double x = GetDouble(); |
| if (x > 0.0) // constraint from ODFF |
| PushDouble( GetLogGamma(x)); |
| else |
| PushIllegalArgument(); |
| } |
| |
| double ScInterpreter::GetBeta(double fAlpha, double fBeta) |
| { |
| double fA; |
| double fB; |
| if (fAlpha > fBeta) |
| { |
| fA = fAlpha; fB = fBeta; |
| } |
| else |
| { |
| fA = fBeta; fB = fAlpha; |
| } |
| if (fA+fB < fMaxGammaArgument) // simple case |
| return GetGamma(fA)/GetGamma(fA+fB)*GetGamma(fB); |
| // need logarithm |
| // GetLogGamma is not accurate enough, back to Lanczos for all three |
| // GetGamma and arrange factors newly. |
| const double fg = 6.024680040776729583740234375; //see GetGamma |
| double fgm = fg - 0.5; |
| double fLanczos = lcl_getLanczosSum(fA); |
| fLanczos /= lcl_getLanczosSum(fA+fB); |
| fLanczos *= lcl_getLanczosSum(fB); |
| double fABgm = fA+fB+fgm; |
| fLanczos *= sqrt((fABgm/(fA+fgm))/(fB+fgm)); |
| double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm)) |
| double fTempB = fA/(fB+fgm); |
| double fResult = exp(-fA * ::boost::math::log1p(fTempA) |
| -fB * ::boost::math::log1p(fTempB)-fgm); |
| fResult *= fLanczos; |
| return fResult; |
| } |
| |
| // Same as GetBeta but with logarithm |
| double ScInterpreter::GetLogBeta(double fAlpha, double fBeta) |
| { |
| double fA; |
| double fB; |
| if (fAlpha > fBeta) |
| { |
| fA = fAlpha; fB = fBeta; |
| } |
| else |
| { |
| fA = fBeta; fB = fAlpha; |
| } |
| const double fg = 6.024680040776729583740234375; //see GetGamma |
| double fgm = fg - 0.5; |
| double fLanczos = lcl_getLanczosSum(fA); |
| fLanczos /= lcl_getLanczosSum(fA+fB); |
| fLanczos *= lcl_getLanczosSum(fB); |
| double fLogLanczos = log(fLanczos); |
| double fABgm = fA+fB+fgm; |
| fLogLanczos += 0.5*(log(fABgm)-log(fA+fgm)-log(fB+fgm)); |
| double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm)) |
| double fTempB = fA/(fB+fgm); |
| double fResult = -fA * ::boost::math::log1p(fTempA) |
| -fB * ::boost::math::log1p(fTempB)-fgm; |
| fResult += fLogLanczos; |
| return fResult; |
| } |
| |
| // beta distribution probability density function |
| double ScInterpreter::GetBetaDistPDF(double fX, double fA, double fB) |
| { |
| // special cases |
| if (fA == 1.0) // result b*(1-x)^(b-1) |
| { |
| if (fB == 1.0) |
| return 1.0; |
| if (fB == 2.0) |
| return -2.0*fX + 2.0; |
| if (fX == 1.0 && fB < 1.0) |
| { |
| SetError(errIllegalArgument); |
| return HUGE_VAL; |
| } |
| if (fX <= 0.01) |
| return fB + fB * ::boost::math::expm1((fB-1.0) * ::boost::math::log1p(-fX)); |
| else |
| return fB * pow(0.5-fX+0.5,fB-1.0); |
| } |
| if (fB == 1.0) // result a*x^(a-1) |
| { |
| if (fA == 2.0) |
| return fA * fX; |
| if (fX == 0.0 && fA < 1.0) |
| { |
| SetError(errIllegalArgument); |
| return HUGE_VAL; |
| } |
| return fA * pow(fX,fA-1); |
| } |
| if (fX <= 0.0) |
| { |
| if (fA < 1.0 && fX == 0.0) |
| { |
| SetError(errIllegalArgument); |
| return HUGE_VAL; |
| } |
| else |
| return 0.0; |
| } |
| if (fX >= 1.0) |
| { |
| if (fB < 1.0 && fX == 1.0) |
| { |
| SetError(errIllegalArgument); |
| return HUGE_VAL; |
| } |
| else |
| return 0.0; |
| } |
| |
| // normal cases; result x^(a-1)*(1-x)^(b-1)/Beta(a,b) |
| const double fLogDblMax = log( ::std::numeric_limits<double>::max()); |
| const double fLogDblMin = log( ::std::numeric_limits<double>::min()); |
| double fLogY = (fX < 0.1) ? ::boost::math::log1p(-fX) : log(0.5-fX+0.5); |
| double fLogX = log(fX); |
| double fAm1LogX = (fA-1.0) * fLogX; |
| double fBm1LogY = (fB-1.0) * fLogY; |
| double fLogBeta = GetLogBeta(fA,fB); |
| // check whether parts over- or underflow |
| if ( fAm1LogX < fLogDblMax && fAm1LogX > fLogDblMin |
| && fBm1LogY < fLogDblMax && fBm1LogY > fLogDblMin |
| && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin |
| && fAm1LogX + fBm1LogY < fLogDblMax && fAm1LogX + fBm1LogY > fLogDblMin) |
| return pow(fX,fA-1.0) * pow(0.5-fX+0.5,fB-1.0) / GetBeta(fA,fB); |
| else // need logarithm; |
| // might overflow as a whole, but seldom, not worth to pre-detect it |
| return exp( fAm1LogX + fBm1LogY - fLogBeta); |
| } |
| |
| |
| /* |
| x^a * (1-x)^b |
| I_x(a,b) = ---------------- * result of ContFrac |
| a * Beta(a,b) |
| */ |
| double lcl_GetBetaHelperContFrac(double fX, double fA, double fB) |
| { // like old version |
| double a1, b1, a2, b2, fnorm, apl2m, d2m, d2m1, cfnew, cf; |
| a1 = 1.0; b1 = 1.0; |
| b2 = 1.0 - (fA+fB)/(fA+1.0)*fX; |
| if (b2 == 0.0) |
| { |
| a2 = 0.0; |
| fnorm = 1.0; |
| cf = 1.0; |
| } |
| else |
| { |
| a2 = 1.0; |
| fnorm = 1.0/b2; |
| cf = a2*fnorm; |
| } |
| cfnew = 1.0; |
| double rm = 1.0; |
| |
| const double fMaxIter = 50000.0; |
| // loop security, normal cases converge in less than 100 iterations. |
| // FIXME: You will get so much iteratons for fX near mean, |
| // I do not know a better algorithm. |
| bool bfinished = false; |
| do |
| { |
| apl2m = fA + 2.0*rm; |
| d2m = rm*(fB-rm)*fX/((apl2m-1.0)*apl2m); |
| d2m1 = -(fA+rm)*(fA+fB+rm)*fX/(apl2m*(apl2m+1.0)); |
| a1 = (a2+d2m*a1)*fnorm; |
| b1 = (b2+d2m*b1)*fnorm; |
| a2 = a1 + d2m1*a2*fnorm; |
| b2 = b1 + d2m1*b2*fnorm; |
| if (b2 != 0.0) |
| { |
| fnorm = 1.0/b2; |
| cfnew = a2*fnorm; |
| bfinished = (fabs(cf-cfnew) < fabs(cf)*fMachEps); |
| } |
| cf = cfnew; |
| rm += 1.0; |
| } |
| while (rm < fMaxIter && !bfinished); |
| return cf; |
| } |
| |
| // cumulative distribution function, normalized |
| double ScInterpreter::GetBetaDist(double fXin, double fAlpha, double fBeta) |
| { |
| // special cases |
| if (fXin <= 0.0) // values are valid, see spec |
| return 0.0; |
| if (fXin >= 1.0) // values are valid, see spec |
| return 1.0; |
| if (fBeta == 1.0) |
| return pow(fXin, fAlpha); |
| if (fAlpha == 1.0) |
| // 1.0 - pow(1.0-fX,fBeta) is not accurate enough |
| return -::boost::math::expm1(fBeta * ::boost::math::log1p(-fXin)); |
| //FIXME: need special algorithm for fX near fP for large fA,fB |
| double fResult; |
| // I use always continued fraction, power series are neither |
| // faster nor more accurate. |
| double fY = (0.5-fXin)+0.5; |
| double flnY = ::boost::math::log1p(-fXin); |
| double fX = fXin; |
| double flnX = log(fXin); |
| double fA = fAlpha; |
| double fB = fBeta; |
| bool bReflect = fXin > fAlpha/(fAlpha+fBeta); |
| if (bReflect) |
| { |
| fA = fBeta; |
| fB = fAlpha; |
| fX = fY; |
| fY = fXin; |
| flnX = flnY; |
| flnY = log(fXin); |
| } |
| fResult = lcl_GetBetaHelperContFrac(fX,fA,fB); |
| fResult = fResult/fA; |
| double fP = fA/(fA+fB); |
| double fQ = fB/(fA+fB); |
| double fTemp; |
| if (fA > 1.0 && fB > 1.0 && fP < 0.97 && fQ < 0.97) //found experimental |
| fTemp = GetBetaDistPDF(fX,fA,fB)*fX*fY; |
| else |
| fTemp = exp(fA*flnX + fB*flnY - GetLogBeta(fA,fB)); |
| fResult *= fTemp; |
| if (bReflect) |
| fResult = 0.5 - fResult + 0.5; |
| if (fResult > 1.0) // ensure valid range |
| fResult = 1.0; |
| if (fResult < 0.0) |
| fResult = 0.0; |
| return fResult; |
| } |
| |
| void ScInterpreter::ScBetaDist() |
| { |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 6 ) ) // expanded, see #i91547# |
| return; |
| double fLowerBound, fUpperBound; |
| double alpha, beta, x; |
| bool bIsCumulative; |
| if (nParamCount == 6) |
| bIsCumulative = GetBool(); |
| else |
| bIsCumulative = true; |
| if (nParamCount >= 5) |
| fUpperBound = GetDouble(); |
| else |
| fUpperBound = 1.0; |
| if (nParamCount >= 4) |
| fLowerBound = GetDouble(); |
| else |
| fLowerBound = 0.0; |
| beta = GetDouble(); |
| alpha = GetDouble(); |
| x = GetDouble(); |
| double fScale = fUpperBound - fLowerBound; |
| if (fScale <= 0.0 || alpha <= 0.0 || beta <= 0.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (bIsCumulative) // cumulative distribution function |
| { |
| // special cases |
| if (x < fLowerBound) |
| { |
| PushDouble(0.0); return; //see spec |
| } |
| if (x > fUpperBound) |
| { |
| PushDouble(1.0); return; //see spec |
| } |
| // normal cases |
| x = (x-fLowerBound)/fScale; // convert to standard form |
| PushDouble(GetBetaDist(x, alpha, beta)); |
| return; |
| } |
| else // probability density function |
| { |
| if (x < fLowerBound || x > fUpperBound) |
| { |
| PushDouble(0.0); |
| return; |
| } |
| x = (x-fLowerBound)/fScale; |
| PushDouble(GetBetaDistPDF(x, alpha, beta)/fScale); |
| return; |
| } |
| } |
| |
| void ScInterpreter::ScPhi() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPhi" ); |
| PushDouble(phi(GetDouble())); |
| } |
| |
| void ScInterpreter::ScGauss() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGauss" ); |
| PushDouble(gauss(GetDouble())); |
| } |
| |
| void ScInterpreter::ScFisher() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFisher" ); |
| double fVal = GetDouble(); |
| if (fabs(fVal) >= 1.0) |
| PushIllegalArgument(); |
| else |
| PushDouble( ::boost::math::atanh( fVal)); |
| } |
| |
| void ScInterpreter::ScFisherInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFisherInv" ); |
| PushDouble( tanh( GetDouble())); |
| } |
| |
| void ScInterpreter::ScFact() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFact" ); |
| double nVal = GetDouble(); |
| if (nVal < 0.0) |
| PushIllegalArgument(); |
| else |
| PushDouble(Fakultaet(nVal)); |
| } |
| |
| void ScInterpreter::ScKombin() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKombin" ); |
| if ( MustHaveParamCount( GetByte(), 2 ) ) |
| { |
| double k = ::rtl::math::approxFloor(GetDouble()); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (k < 0.0 || n < 0.0 || k > n) |
| PushIllegalArgument(); |
| else |
| PushDouble(BinomKoeff(n, k)); |
| } |
| } |
| |
| void ScInterpreter::ScKombin2() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKombin2" ); |
| if ( MustHaveParamCount( GetByte(), 2 ) ) |
| { |
| double k = ::rtl::math::approxFloor(GetDouble()); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (k < 0.0 || n < 0.0 || k > n) |
| PushIllegalArgument(); |
| else |
| PushDouble(BinomKoeff(n + k - 1, k)); |
| } |
| } |
| |
| void ScInterpreter::ScVariationen() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVariationen" ); |
| if ( MustHaveParamCount( GetByte(), 2 ) ) |
| { |
| double k = ::rtl::math::approxFloor(GetDouble()); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (n < 0.0 || k < 0.0 || k > n) |
| PushIllegalArgument(); |
| else if (k == 0.0) |
| PushInt(1); // (n! / (n - 0)!) == 1 |
| else |
| { |
| double nVal = n; |
| for (sal_uLong i = (sal_uLong)k-1; i >= 1; i--) |
| nVal *= n-(double)i; |
| PushDouble(nVal); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScVariationen2() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVariationen2" ); |
| if ( MustHaveParamCount( GetByte(), 2 ) ) |
| { |
| double k = ::rtl::math::approxFloor(GetDouble()); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (n < 0.0 || k < 0.0 || k > n) |
| PushIllegalArgument(); |
| else |
| PushDouble(pow(n,k)); |
| } |
| } |
| |
| |
| double ScInterpreter::GetBinomDistPMF(double x, double n, double p) |
| // used in ScB and ScBinomDist |
| // preconditions: 0.0 <= x <= n, 0.0 < p < 1.0; x,n integral although double |
| { |
| double q = (0.5 - p) + 0.5; |
| double fFactor = pow(q, n); |
| if (fFactor <=::std::numeric_limits<double>::min()) |
| { |
| fFactor = pow(p, n); |
| if (fFactor <= ::std::numeric_limits<double>::min()) |
| return GetBetaDistPDF(p, x+1.0, n-x+1.0)/(n+1.0); |
| else |
| { |
| sal_uInt32 max = static_cast<sal_uInt32>(n - x); |
| for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++) |
| fFactor *= (n-i)/(i+1)*q/p; |
| return fFactor; |
| } |
| } |
| else |
| { |
| sal_uInt32 max = static_cast<sal_uInt32>(x); |
| for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++) |
| fFactor *= (n-i)/(i+1)*p/q; |
| return fFactor; |
| } |
| } |
| |
| double lcl_GetBinomDistRange(double n, double xs,double xe, |
| double fFactor /* q^n */, double p, double q) |
| //preconditions: 0.0 <= xs < xe <= n; xs,xe,n integral although double |
| { |
| sal_uInt32 i; |
| double fSum; |
| // skip summands index 0 to xs-1, start sum with index xs |
| sal_uInt32 nXs = static_cast<sal_uInt32>( xs ); |
| for (i = 1; i <= nXs && fFactor > 0.0; i++) |
| fFactor *= (n-i+1)/i * p/q; |
| fSum = fFactor; // Summand xs |
| sal_uInt32 nXe = static_cast<sal_uInt32>(xe); |
| for (i = nXs+1; i <= nXe && fFactor > 0.0; i++) |
| { |
| fFactor *= (n-i+1)/i * p/q; |
| fSum += fFactor; |
| } |
| return (fSum>1.0) ? 1.0 : fSum; |
| } |
| |
| void ScInterpreter::ScB() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScB" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) |
| return ; |
| if (nParamCount == 3) // mass function |
| { |
| double x = ::rtl::math::approxFloor(GetDouble()); |
| double p = GetDouble(); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0) |
| PushIllegalArgument(); |
| else |
| if (p == 0.0) |
| PushDouble( (x == 0.0) ? 1.0 : 0.0 ); |
| else |
| if ( p == 1.0) |
| PushDouble( (x == n) ? 1.0 : 0.0); |
| else |
| PushDouble(GetBinomDistPMF(x,n,p)); |
| } |
| else |
| { // nParamCount == 4 |
| double xe = ::rtl::math::approxFloor(GetDouble()); |
| double xs = ::rtl::math::approxFloor(GetDouble()); |
| double p = GetDouble(); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| double q = (0.5 - p) + 0.5; |
| bool bIsValidX = ( 0.0 <= xs && xs <= xe && xe <= n); |
| if ( bIsValidX && 0.0 < p && p < 1.0) |
| { |
| if (xs == xe) // mass function |
| PushDouble(GetBinomDistPMF(xs,n,p)); |
| else |
| { |
| double fFactor = pow(q, n); |
| if (fFactor > ::std::numeric_limits<double>::min()) |
| PushDouble(lcl_GetBinomDistRange(n,xs,xe,fFactor,p,q)); |
| else |
| { |
| fFactor = pow(p, n); |
| if (fFactor > ::std::numeric_limits<double>::min()) |
| { |
| // sum from j=xs to xe {(n choose j) * p^j * q^(n-j)} |
| // = sum from i = n-xe to n-xs { (n choose i) * q^i * p^(n-i)} |
| PushDouble(lcl_GetBinomDistRange(n,n-xe,n-xs,fFactor,q,p)); |
| } |
| else |
| PushDouble(GetBetaDist(q,n-xe,xe+1.0)-GetBetaDist(q,n-xs+1,xs) ); |
| } |
| } |
| } |
| else |
| { |
| if ( bIsValidX ) // not(0<p<1) |
| { |
| if ( p == 0.0 ) |
| PushDouble( (xs == 0.0) ? 1.0 : 0.0 ); |
| else if ( p == 1.0 ) |
| PushDouble( (xe == n) ? 1.0 : 0.0 ); |
| else |
| PushIllegalArgument(); |
| } |
| else |
| PushIllegalArgument(); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScBinomDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBinomDist" ); |
| if ( MustHaveParamCount( GetByte(), 4 ) ) |
| { |
| bool bIsCum = GetBool(); // false=mass function; true=cumulative |
| double p = GetDouble(); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| double x = ::rtl::math::approxFloor(GetDouble()); |
| double q = (0.5 - p) + 0.5; // get one bit more for p near 1.0 |
| double fFactor, fSum; |
| if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if ( p == 0.0) |
| { |
| PushDouble( (x==0.0 || bIsCum) ? 1.0 : 0.0 ); |
| return; |
| } |
| if ( p == 1.0) |
| { |
| PushDouble( (x==n) ? 1.0 : 0.0); |
| return; |
| } |
| if (!bIsCum) |
| PushDouble( GetBinomDistPMF(x,n,p)); |
| else |
| { |
| if (x == n) |
| PushDouble(1.0); |
| else |
| { |
| fFactor = pow(q, n); |
| if (x == 0.0) |
| PushDouble(fFactor); |
| else |
| if (fFactor <= ::std::numeric_limits<double>::min()) |
| { |
| fFactor = pow(p, n); |
| if (fFactor <= ::std::numeric_limits<double>::min()) |
| PushDouble(GetBetaDist(q,n-x,x+1.0)); |
| else |
| { |
| if (fFactor > fMachEps) |
| { |
| fSum = 1.0 - fFactor; |
| sal_uInt32 max = static_cast<sal_uInt32> (n - x) - 1; |
| for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++) |
| { |
| fFactor *= (n-i)/(i+1)*q/p; |
| fSum -= fFactor; |
| } |
| PushDouble( (fSum < 0.0) ? 0.0 : fSum ); |
| } |
| else |
| PushDouble(lcl_GetBinomDistRange(n,n-x,n,fFactor,q,p)); |
| } |
| } |
| else |
| PushDouble( lcl_GetBinomDistRange(n,0.0,x,fFactor,p,q)) ; |
| } |
| } |
| } |
| } |
| |
| void ScInterpreter::ScCritBinom() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCritBinom" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double alpha = GetDouble(); // alpha |
| double p = GetDouble(); // p |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| if (n < 0.0 || alpha <= 0.0 || alpha >= 1.0 || p < 0.0 || p > 1.0) |
| PushIllegalArgument(); |
| else |
| { |
| double q = 1.0 - p; |
| double fFactor = pow(q,n); |
| if (fFactor == 0.0) |
| { |
| fFactor = pow(p, n); |
| if (fFactor == 0.0) |
| PushNoValue(); |
| else |
| { |
| double fSum = 1.0 - fFactor; sal_uLong max = (sal_uLong) n; |
| sal_uLong i; |
| |
| for ( i = 0; i < max && fSum >= alpha; i++) |
| { |
| fFactor *= (n-i)/(i+1)*q/p; |
| fSum -= fFactor; |
| } |
| PushDouble(n-i); |
| } |
| } |
| else |
| { |
| double fSum = fFactor; sal_uLong max = (sal_uLong) n; |
| sal_uLong i; |
| |
| for ( i = 0; i < max && fSum < alpha; i++) |
| { |
| fFactor *= (n-i)/(i+1)*p/q; |
| fSum += fFactor; |
| } |
| PushDouble(i); |
| } |
| } |
| } |
| } |
| |
| void ScInterpreter::ScNegBinomDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNegBinomDist" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double p = GetDouble(); // p |
| double r = GetDouble(); // r |
| double x = GetDouble(); // x |
| if (r < 0.0 || x < 0.0 || p < 0.0 || p > 1.0) |
| PushIllegalArgument(); |
| else |
| { |
| double q = 1.0 - p; |
| double fFactor = pow(p,r); |
| for (double i = 0.0; i < x; i++) |
| fFactor *= (i+r)/(i+1.0)*q; |
| PushDouble(fFactor); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScNormDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNormDist" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 4)) |
| return; |
| bool bCumulative = nParamCount == 4 ? GetBool() : true; |
| double sigma = GetDouble(); // standard deviation |
| double mue = GetDouble(); // mean |
| double x = GetDouble(); // x |
| if (sigma <= 0.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (bCumulative) |
| PushDouble(integralPhi((x-mue)/sigma)); |
| else |
| PushDouble(phi((x-mue)/sigma)/sigma); |
| } |
| |
| void ScInterpreter::ScLogNormDist() //expanded, see #i100119# |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogNormDist" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 1, 4)) |
| return; |
| bool bCumulative = nParamCount == 4 ? GetBool() : true; // cumulative |
| double sigma = nParamCount >= 3 ? GetDouble() : 1.0; // standard deviation |
| double mue = nParamCount >= 2 ? GetDouble() : 0.0; // mean |
| double x = GetDouble(); // x |
| if (sigma <= 0.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (bCumulative) |
| { // cumulative |
| if (x <= 0.0) |
| PushDouble(0.0); |
| else |
| PushDouble(integralPhi((log(x)-mue)/sigma)); |
| } |
| else |
| { // density |
| if (x <= 0.0) |
| PushIllegalArgument(); |
| else |
| PushDouble(phi((log(x)-mue)/sigma)/sigma/x); |
| } |
| } |
| |
| void ScInterpreter::ScStdNormDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStdNormDist" ); |
| PushDouble(integralPhi(GetDouble())); |
| } |
| |
| void ScInterpreter::ScExpDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExpDist" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double kum = GetDouble(); // 0 oder 1 |
| double lambda = GetDouble(); // lambda |
| double x = GetDouble(); // x |
| if (lambda <= 0.0) |
| PushIllegalArgument(); |
| else if (kum == 0.0) // Dichte |
| { |
| if (x >= 0.0) |
| PushDouble(lambda * exp(-lambda*x)); |
| else |
| PushInt(0); |
| } |
| else // Verteilung |
| { |
| if (x > 0.0) |
| PushDouble(1.0 - exp(-lambda*x)); |
| else |
| PushInt(0); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScTDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTDist" ); |
| if ( !MustHaveParamCount( GetByte(), 3 ) ) |
| return; |
| double fFlag = ::rtl::math::approxFloor(GetDouble()); |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| double T = GetDouble(); |
| if (fDF < 1.0 || T < 0.0 || (fFlag != 1.0 && fFlag != 2.0) ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| double R = GetTDist(T, fDF); |
| if (fFlag == 1.0) |
| PushDouble(R); |
| else |
| PushDouble(2.0*R); |
| } |
| |
| void ScInterpreter::ScFDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFDist" ); |
| if ( !MustHaveParamCount( GetByte(), 3 ) ) |
| return; |
| double fF2 = ::rtl::math::approxFloor(GetDouble()); |
| double fF1 = ::rtl::math::approxFloor(GetDouble()); |
| double fF = GetDouble(); |
| if (fF < 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| PushDouble(GetFDist(fF, fF1, fF2)); |
| } |
| |
| void ScInterpreter::ScChiDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiDist" ); |
| double fResult; |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| double fChi = GetDouble(); |
| if (fDF < 1.0) // x<=0 returns 1, see ODFF 6.17.10 |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| fResult = GetChiDist( fChi, fDF); |
| if (nGlobalError) |
| { |
| PushError( nGlobalError); |
| return; |
| } |
| PushDouble(fResult); |
| } |
| |
| void ScInterpreter::ScWeibull() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScWeibull" ); |
| if ( MustHaveParamCount( GetByte(), 4 ) ) |
| { |
| double kum = GetDouble(); // 0 oder 1 |
| double beta = GetDouble(); // beta |
| double alpha = GetDouble(); // alpha |
| double x = GetDouble(); // x |
| if (alpha <= 0.0 || beta <= 0.0 || x < 0.0) |
| PushIllegalArgument(); |
| else if (kum == 0.0) // Dichte |
| PushDouble(alpha/pow(beta,alpha)*pow(x,alpha-1.0)* |
| exp(-pow(x/beta,alpha))); |
| else // Verteilung |
| PushDouble(1.0 - exp(-pow(x/beta,alpha))); |
| } |
| } |
| |
| void ScInterpreter::ScPoissonDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPoissonDist" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( MustHaveParamCount( nParamCount, 2, 3 ) ) |
| { |
| bool bCumulative = (nParamCount == 3 ? GetBool() : true); // default cumulative |
| double lambda = GetDouble(); // Mean |
| double x = ::rtl::math::approxFloor(GetDouble()); // discrete distribution |
| if (lambda < 0.0 || x < 0.0) |
| PushIllegalArgument(); |
| else if (!bCumulative) // Probability mass function |
| { |
| if (lambda == 0.0) |
| PushInt(0); |
| else |
| { |
| if (lambda >712) // underflow in exp(-lambda) |
| { // accuracy 11 Digits |
| PushDouble( exp(x*log(lambda)-lambda-GetLogGamma(x+1.0))); |
| } |
| else |
| { |
| double fPoissonVar = 1.0; |
| for ( double f = 0.0; f < x; ++f ) |
| fPoissonVar *= lambda / ( f + 1.0 ); |
| PushDouble( fPoissonVar * exp( -lambda ) ); |
| } |
| } |
| } |
| else // Cumulative distribution function |
| { |
| if (lambda == 0.0) |
| PushInt(1); |
| else |
| { |
| if (lambda > 712 ) // underflow in exp(-lambda) |
| { // accuracy 12 Digits |
| PushDouble(GetUpRegIGamma(x+1.0,lambda)); |
| } |
| else |
| { |
| if (x >= 936.0) // result is always undistinghable from 1 |
| PushDouble (1.0); |
| else |
| { |
| double fSummand = exp(-lambda); |
| double fSum = fSummand; |
| int nEnd = sal::static_int_cast<int>( x ); |
| for (int i = 1; i <= nEnd; i++) |
| { |
| fSummand = (fSummand * lambda)/(double)i; |
| fSum += fSummand; |
| } |
| PushDouble(fSum); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** Local function used in the calculation of the hypergeometric distribution. |
| */ |
| void lcl_PutFactorialElements( ::std::vector< double >& cn, double fLower, double fUpper, double fBase ) |
| { |
| for ( double i = fLower; i <= fUpper; ++i ) |
| { |
| double fVal = fBase - i; |
| if ( fVal > 1.0 ) |
| cn.push_back( fVal ); |
| } |
| } |
| |
| /** Calculates a value of the hypergeometric distribution. |
| |
| The algorithm is designed to avoid unnecessary multiplications and division |
| by expanding all factorial elements (9 of them). It is done by excluding |
| those ranges that overlap in the numerator and the denominator. This allows |
| for a fast calculation for large values which would otherwise cause an overflow |
| in the intermediate values. |
| |
| @author Kohei Yoshida <kohei@openoffice.org> |
| |
| @see #i47296# |
| |
| */ |
| void ScInterpreter::ScHypGeomDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHypGeomDist" ); |
| const size_t nMaxArraySize = 500000; // arbitrary max array size |
| |
| if ( !MustHaveParamCount( GetByte(), 4 ) ) |
| return; |
| |
| double N = ::rtl::math::approxFloor(GetDouble()); |
| double M = ::rtl::math::approxFloor(GetDouble()); |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| double x = ::rtl::math::approxFloor(GetDouble()); |
| |
| if( (x < 0.0) || (n < x) || (M < x) || (N < n) || (N < M) || (x < n - N + M) ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| typedef ::std::vector< double > HypContainer; |
| HypContainer cnNumer, cnDenom; |
| |
| size_t nEstContainerSize = static_cast<size_t>( x + ::std::min( n, M ) ); |
| size_t nMaxSize = ::std::min( cnNumer.max_size(), nMaxArraySize ); |
| if ( nEstContainerSize > nMaxSize ) |
| { |
| PushNoValue(); |
| return; |
| } |
| cnNumer.reserve( nEstContainerSize + 10 ); |
| cnDenom.reserve( nEstContainerSize + 10 ); |
| |
| // Trim coefficient C first |
| double fCNumVarUpper = N - n - M + x - 1.0; |
| double fCDenomVarLower = 1.0; |
| if ( N - n - M + x >= M - x + 1.0 ) |
| { |
| fCNumVarUpper = M - x - 1.0; |
| fCDenomVarLower = N - n - 2.0*(M - x) + 1.0; |
| } |
| |
| #ifdef DBG_UTIL |
| double fCNumLower = N - n - fCNumVarUpper; |
| #endif |
| double fCDenomUpper = N - n - M + x + 1.0 - fCDenomVarLower; |
| |
| double fDNumVarLower = n - M; |
| |
| if ( n >= M + 1.0 ) |
| { |
| if ( N - M < n + 1.0 ) |
| { |
| // Case 1 |
| |
| if ( N - n < n + 1.0 ) |
| { |
| // no overlap |
| lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, N - n - 1.0, N ); |
| } |
| else |
| { |
| // overlap |
| DBG_ASSERT( fCNumLower < n + 1.0, "ScHypGeomDist: wrong assertion" ); |
| lcl_PutFactorialElements( cnNumer, N - 2.0*n, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); |
| } |
| |
| DBG_ASSERT( fCDenomUpper <= N - M, "ScHypGeomDist: wrong assertion" ); |
| |
| if ( fCDenomUpper < n - x + 1.0 ) |
| // no overlap |
| lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 ); |
| else |
| { |
| // overlap |
| lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 ); |
| |
| fCDenomUpper = n - x; |
| fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; |
| } |
| } |
| else |
| { |
| // Case 2 |
| |
| if ( n > M - 1.0 ) |
| { |
| // no overlap |
| lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N ); |
| } |
| else |
| { |
| lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); |
| } |
| |
| DBG_ASSERT( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" ); |
| |
| if ( fCDenomUpper < n - x + 1.0 ) |
| // no overlap |
| lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - n + x, N - M + 1.0 ); |
| else |
| { |
| lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - fCDenomUpper, N - M + 1.0 ); |
| fCDenomUpper = n - x; |
| fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; |
| } |
| } |
| |
| DBG_ASSERT( fCDenomUpper <= M, "ScHypGeomDist: wrong assertion" ); |
| } |
| else |
| { |
| if ( N - M < M + 1.0 ) |
| { |
| // Case 3 |
| |
| if ( N - n < M + 1.0 ) |
| { |
| // No overlap |
| lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, N - M - 1.0, N ); |
| } |
| else |
| { |
| lcl_PutFactorialElements( cnNumer, N - n - M, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); |
| } |
| |
| if ( n - x + 1.0 > fCDenomUpper ) |
| // No overlap |
| lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 ); |
| else |
| { |
| // Overlap |
| lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 ); |
| |
| fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; |
| fCDenomUpper = n - x; |
| } |
| } |
| else |
| { |
| // Case 4 |
| |
| DBG_ASSERT( M >= n - x, "ScHypGeomDist: wrong assertion" ); |
| DBG_ASSERT( M - x <= N - M + 1.0, "ScHypGeomDist: wrong assertion" ); |
| |
| if ( N - n < N - M + 1.0 ) |
| { |
| // No overlap |
| lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N ); |
| } |
| else |
| { |
| // Overlap |
| DBG_ASSERT( fCNumLower <= N - M + 1.0, "ScHypGeomDist: wrong assertion" ); |
| |
| lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n ); |
| lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); |
| } |
| |
| if ( n - x + 1.0 > fCDenomUpper ) |
| // No overlap |
| lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - n + x, N - M + 1.0 ); |
| else if ( M >= fCDenomUpper ) |
| { |
| lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - fCDenomUpper, N - M + 1.0 ); |
| |
| fCDenomUpper = n - x; |
| fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; |
| } |
| else |
| { |
| DBG_ASSERT( M <= fCDenomUpper, "ScHypGeomDist: wrong assertion" ); |
| lcl_PutFactorialElements( cnDenom, fCDenomVarLower, N - n - 2.0*M + x, |
| N - n - M + x + 1.0 ); |
| |
| fCDenomUpper = n - x; |
| fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; |
| } |
| } |
| |
| DBG_ASSERT( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" ); |
| |
| fDNumVarLower = 0.0; |
| } |
| |
| double nDNumVarUpper = fCDenomUpper < x + 1.0 ? n - x - 1.0 : n - fCDenomUpper - 1.0; |
| double nDDenomVarLower = fCDenomUpper < x + 1.0 ? fCDenomVarLower : N - n - M + 1.0; |
| lcl_PutFactorialElements( cnNumer, fDNumVarLower, nDNumVarUpper, n ); |
| lcl_PutFactorialElements( cnDenom, nDDenomVarLower, N - n - M + x, N - n - M + x + 1.0 ); |
| |
| ::std::sort( cnNumer.begin(), cnNumer.end() ); |
| ::std::sort( cnDenom.begin(), cnDenom.end() ); |
| HypContainer::reverse_iterator it1 = cnNumer.rbegin(), it1End = cnNumer.rend(); |
| HypContainer::reverse_iterator it2 = cnDenom.rbegin(), it2End = cnDenom.rend(); |
| |
| double fFactor = 1.0; |
| for ( ; it1 != it1End || it2 != it2End; ) |
| { |
| double fEnum = 1.0, fDenom = 1.0; |
| if ( it1 != it1End ) |
| fEnum = *it1++; |
| if ( it2 != it2End ) |
| fDenom = *it2++; |
| fFactor *= fEnum / fDenom; |
| } |
| |
| PushDouble(fFactor); |
| } |
| |
| void ScInterpreter::ScGammaDist() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGammaDist" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) |
| return; |
| double bCumulative; |
| if (nParamCount == 4) |
| bCumulative = GetBool(); |
| else |
| bCumulative = true; |
| double fBeta = GetDouble(); // scale |
| double fAlpha = GetDouble(); // shape |
| double fX = GetDouble(); // x |
| if (fAlpha <= 0.0 || fBeta <= 0.0) |
| PushIllegalArgument(); |
| else |
| { |
| if (bCumulative) // distribution |
| PushDouble( GetGammaDist( fX, fAlpha, fBeta)); |
| else // density |
| PushDouble( GetGammaDistPDF( fX, fAlpha, fBeta)); |
| } |
| } |
| |
| void ScInterpreter::ScNormInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNormInv" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double sigma = GetDouble(); |
| double mue = GetDouble(); |
| double x = GetDouble(); |
| if (sigma <= 0.0 || x < 0.0 || x > 1.0) |
| PushIllegalArgument(); |
| else if (x == 0.0 || x == 1.0) |
| PushNoValue(); |
| else |
| PushDouble(gaussinv(x)*sigma + mue); |
| } |
| } |
| |
| void ScInterpreter::ScSNormInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSNormInv" ); |
| double x = GetDouble(); |
| if (x < 0.0 || x > 1.0) |
| PushIllegalArgument(); |
| else if (x == 0.0 || x == 1.0) |
| PushNoValue(); |
| else |
| PushDouble(gaussinv(x)); |
| } |
| |
| void ScInterpreter::ScLogNormInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogNormInv" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double sigma = GetDouble(); // Stdabw |
| double mue = GetDouble(); // Mittelwert |
| double y = GetDouble(); // y |
| if (sigma <= 0.0 || y <= 0.0 || y >= 1.0) |
| PushIllegalArgument(); |
| else |
| PushDouble(exp(mue+sigma*gaussinv(y))); |
| } |
| } |
| |
| class ScGammaDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fAlpha, fBeta; |
| |
| public: |
| ScGammaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) : |
| rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {} |
| |
| double GetValue( double x ) const { return fp - rInt.GetGammaDist(x, fAlpha, fBeta); } |
| }; |
| |
| void ScInterpreter::ScGammaInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGammaInv" ); |
| if ( !MustHaveParamCount( GetByte(), 3 ) ) |
| return; |
| double fBeta = GetDouble(); |
| double fAlpha = GetDouble(); |
| double fP = GetDouble(); |
| if (fAlpha <= 0.0 || fBeta <= 0.0 || fP < 0.0 || fP >= 1.0 ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (fP == 0.0) |
| PushInt(0); |
| else |
| { |
| bool bConvError; |
| ScGammaDistFunction aFunc( *this, fP, fAlpha, fBeta ); |
| double fStart = fAlpha * fBeta; |
| double fVal = lcl_IterateInverse( aFunc, fStart*0.5, fStart, bConvError ); |
| if (bConvError) |
| SetError(errNoConvergence); |
| PushDouble(fVal); |
| } |
| } |
| |
| class ScBetaDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fAlpha, fBeta; |
| |
| public: |
| ScBetaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) : |
| rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {} |
| |
| double GetValue( double x ) const { return fp - rInt.GetBetaDist(x, fAlpha, fBeta); } |
| }; |
| |
| void ScInterpreter::ScBetaInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBetaInv" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) |
| return; |
| double fP, fA, fB, fAlpha, fBeta; |
| if (nParamCount == 5) |
| fB = GetDouble(); |
| else |
| fB = 1.0; |
| if (nParamCount >= 4) |
| fA = GetDouble(); |
| else |
| fA = 0.0; |
| fBeta = GetDouble(); |
| fAlpha = GetDouble(); |
| fP = GetDouble(); |
| if (fP < 0.0 || fP >= 1.0 || fA == fB || fAlpha <= 0.0 || fBeta <= 0.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (fP == 0.0) |
| PushInt(0); |
| else |
| { |
| bool bConvError; |
| ScBetaDistFunction aFunc( *this, fP, fAlpha, fBeta ); |
| // 0..1 as range for iteration so it isn't extended beyond the valid range |
| double fVal = lcl_IterateInverse( aFunc, 0.0, 1.0, bConvError ); |
| if (bConvError) |
| PushError( errNoConvergence); |
| else |
| PushDouble(fA + fVal*(fB-fA)); // scale to (A,B) |
| } |
| } |
| |
| // Achtung: T, F und Chi |
| // sind monoton fallend, |
| // deshalb 1-Dist als Funktion |
| |
| class ScTDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fDF; |
| |
| public: |
| ScTDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : |
| rInt(rI), fp(fpVal), fDF(fDFVal) {} |
| |
| double GetValue( double x ) const { return fp - 2 * rInt.GetTDist(x, fDF); } |
| }; |
| |
| void ScInterpreter::ScTInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTInv" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| double fP = GetDouble(); |
| if (fDF < 1.0 || fDF >= 1.0E5 || fP <= 0.0 || fP > 1.0 ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| bool bConvError; |
| ScTDistFunction aFunc( *this, fP, fDF ); |
| double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); |
| if (bConvError) |
| SetError(errNoConvergence); |
| PushDouble(fVal); |
| } |
| |
| class ScFDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fF1, fF2; |
| |
| public: |
| ScFDistFunction( ScInterpreter& rI, double fpVal, double fF1Val, double fF2Val ) : |
| rInt(rI), fp(fpVal), fF1(fF1Val), fF2(fF2Val) {} |
| |
| double GetValue( double x ) const { return fp - rInt.GetFDist(x, fF1, fF2); } |
| }; |
| |
| void ScInterpreter::ScFInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFInv" ); |
| if ( !MustHaveParamCount( GetByte(), 3 ) ) |
| return; |
| double fF2 = ::rtl::math::approxFloor(GetDouble()); |
| double fF1 = ::rtl::math::approxFloor(GetDouble()); |
| double fP = GetDouble(); |
| if (fP <= 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10 || fP > 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| bool bConvError; |
| ScFDistFunction aFunc( *this, fP, fF1, fF2 ); |
| double fVal = lcl_IterateInverse( aFunc, fF1*0.5, fF1, bConvError ); |
| if (bConvError) |
| SetError(errNoConvergence); |
| PushDouble(fVal); |
| } |
| |
| class ScChiDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fDF; |
| |
| public: |
| ScChiDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : |
| rInt(rI), fp(fpVal), fDF(fDFVal) {} |
| |
| double GetValue( double x ) const { return fp - rInt.GetChiDist(x, fDF); } |
| }; |
| |
| void ScInterpreter::ScChiInv() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiInv" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| double fP = GetDouble(); |
| if (fDF < 1.0 || fP <= 0.0 || fP > 1.0 ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| bool bConvError; |
| ScChiDistFunction aFunc( *this, fP, fDF ); |
| double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); |
| if (bConvError) |
| SetError(errNoConvergence); |
| PushDouble(fVal); |
| } |
| |
| /***********************************************/ |
| class ScChiSqDistFunction : public ScDistFunc |
| { |
| ScInterpreter& rInt; |
| double fp, fDF; |
| |
| public: |
| ScChiSqDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : |
| rInt(rI), fp(fpVal), fDF(fDFVal) {} |
| |
| double GetValue( double x ) const { return fp - rInt.GetChiSqDistCDF(x, fDF); } |
| }; |
| |
| |
| void ScInterpreter::ScChiSqInv() |
| { |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double fDF = ::rtl::math::approxFloor(GetDouble()); |
| double fP = GetDouble(); |
| if (fDF < 1.0 || fP < 0.0 || fP >= 1.0 ) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| bool bConvError; |
| ScChiSqDistFunction aFunc( *this, fP, fDF ); |
| double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); |
| if (bConvError) |
| SetError(errNoConvergence); |
| PushDouble(fVal); |
| } |
| |
| |
| void ScInterpreter::ScConfidence() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScConfidence" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double n = ::rtl::math::approxFloor(GetDouble()); |
| double sigma = GetDouble(); |
| double alpha = GetDouble(); |
| if (sigma <= 0.0 || alpha <= 0.0 || alpha >= 1.0 || n < 1.0) |
| PushIllegalArgument(); |
| else |
| PushDouble( gaussinv(1.0-alpha/2.0) * sigma/sqrt(n) ); |
| } |
| } |
| |
| void ScInterpreter::ScZTest() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZTest" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) |
| return; |
| double sigma = 0.0, mue, x; |
| if (nParamCount == 3) |
| { |
| sigma = GetDouble(); |
| if (sigma <= 0.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| } |
| x = GetDouble(); |
| |
| double fSum = 0.0; |
| double fSumSqr = 0.0; |
| double fVal; |
| double rValCount = 0.0; |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| { |
| fVal = GetDouble(); |
| fSum += fVal; |
| fSumSqr += fVal*fVal; |
| rValCount++; |
| } |
| break; |
| case svSingleRef : |
| { |
| ScAddress aAdr; |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| fVal = GetCellValue( aAdr, pCell ); |
| fSum += fVal; |
| fSumSqr += fVal*fVal; |
| rValCount++; |
| } |
| } |
| break; |
| case svRefList : |
| case formula::svDoubleRef : |
| { |
| short nParam = 1; |
| size_t nRefInList = 0; |
| while (nParam-- > 0) |
| { |
| ScRange aRange; |
| sal_uInt16 nErr = 0; |
| PopDoubleRef( aRange, nParam, nRefInList); |
| ScValueIterator aValIter(pDok, aRange, glSubTotal); |
| if (aValIter.GetFirst(fVal, nErr)) |
| { |
| fSum += fVal; |
| fSumSqr += fVal*fVal; |
| rValCount++; |
| while ((nErr == 0) && aValIter.GetNext(fVal, nErr)) |
| { |
| fSum += fVal; |
| fSumSqr += fVal*fVal; |
| rValCount++; |
| } |
| SetError(nErr); |
| } |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for ( SCSIZE i = 0; i < nCount; i++ ) |
| { |
| fVal= pMat->GetDouble(i); |
| fSum += fVal; |
| fSumSqr += fVal * fVal; |
| rValCount++; |
| } |
| } |
| else |
| { |
| for (SCSIZE i = 0; i < nCount; i++) |
| if (!pMat->IsString(i)) |
| { |
| fVal= pMat->GetDouble(i); |
| fSum += fVal; |
| fSumSqr += fVal * fVal; |
| rValCount++; |
| } |
| } |
| } |
| } |
| break; |
| default : SetError(errIllegalParameter); break; |
| } |
| if (rValCount <= 1.0) |
| PushError( errDivisionByZero); |
| else |
| { |
| mue = fSum/rValCount; |
| if (nParamCount != 3) |
| { |
| sigma = (fSumSqr - fSum*fSum/rValCount)/(rValCount-1.0); |
| PushDouble(0.5 - gauss((mue-x)/sqrt(sigma/rValCount))); |
| } |
| else |
| PushDouble(0.5 - gauss((mue-x)*sqrt(rValCount)/sigma)); |
| } |
| } |
| bool ScInterpreter::CalculateTest(sal_Bool _bTemplin |
| ,const SCSIZE nC1, const SCSIZE nC2,const SCSIZE nR1,const SCSIZE nR2 |
| ,const ScMatrixRef& pMat1,const ScMatrixRef& pMat2 |
| ,double& fT,double& fF) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateTest" ); |
| double fCount1 = 0.0; |
| double fCount2 = 0.0; |
| double fSum1 = 0.0; |
| double fSumSqr1 = 0.0; |
| double fSum2 = 0.0; |
| double fSumSqr2 = 0.0; |
| double fVal; |
| SCSIZE i,j; |
| for (i = 0; i < nC1; i++) |
| for (j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j)) |
| { |
| fVal = pMat1->GetDouble(i,j); |
| fSum1 += fVal; |
| fSumSqr1 += fVal * fVal; |
| fCount1++; |
| } |
| } |
| for (i = 0; i < nC2; i++) |
| for (j = 0; j < nR2; j++) |
| { |
| if (!pMat2->IsString(i,j)) |
| { |
| fVal = pMat2->GetDouble(i,j); |
| fSum2 += fVal; |
| fSumSqr2 += fVal * fVal; |
| fCount2++; |
| } |
| } |
| if (fCount1 < 2.0 || fCount2 < 2.0) |
| { |
| PushNoValue(); |
| return false; |
| } // if (fCount1 < 2.0 || fCount2 < 2.0) |
| if ( _bTemplin ) |
| { |
| double fS1 = (fSumSqr1-fSum1*fSum1/fCount1)/(fCount1-1.0)/fCount1; |
| double fS2 = (fSumSqr2-fSum2*fSum2/fCount2)/(fCount2-1.0)/fCount2; |
| if (fS1 + fS2 == 0.0) |
| { |
| PushNoValue(); |
| return false; |
| } |
| fT = fabs(fSum1/fCount1 - fSum2/fCount2)/sqrt(fS1+fS2); |
| double c = fS1/(fS1+fS2); |
| // s.u. fF = ::rtl::math::approxFloor(1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)/(fCount2-1.0))); |
| // fF = ::rtl::math::approxFloor((fS1+fS2)*(fS1+fS2)/(fS1*fS1/(fCount1-1.0) + fS2*fS2/(fCount2-1.0))); |
| |
| // GetTDist wird mit GetBetaDist berechnet und kommt auch mit nicht ganzzahligen |
| // Freiheitsgraden klar. Dann stimmt das Ergebnis auch mit Excel ueberein (#52406#): |
| fF = 1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)/(fCount2-1.0)); |
| } |
| else |
| { |
| // laut Bronstein-Semendjajew |
| double fS1 = (fSumSqr1 - fSum1*fSum1/fCount1) / (fCount1 - 1.0); // Varianz |
| double fS2 = (fSumSqr2 - fSum2*fSum2/fCount2) / (fCount2 - 1.0); |
| fT = fabs( fSum1/fCount1 - fSum2/fCount2 ) / |
| sqrt( (fCount1-1.0)*fS1 + (fCount2-1.0)*fS2 ) * |
| sqrt( fCount1*fCount2*(fCount1+fCount2-2)/(fCount1+fCount2) ); |
| fF = fCount1 + fCount2 - 2; |
| } |
| return true; |
| } |
| void ScInterpreter::ScTTest() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTTest" ); |
| if ( !MustHaveParamCount( GetByte(), 4 ) ) |
| return; |
| double fTyp = ::rtl::math::approxFloor(GetDouble()); |
| double fAnz = ::rtl::math::approxFloor(GetDouble()); |
| if (fAnz != 1.0 && fAnz != 2.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| ScMatrixRef pMat2 = GetMatrix(); |
| ScMatrixRef pMat1 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| double fT, fF; |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| SCSIZE i, j; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| if (fTyp == 1.0) |
| { |
| if (nC1 != nC2 || nR1 != nR2) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| double fCount = 0.0; |
| double fSum1 = 0.0; |
| double fSum2 = 0.0; |
| double fSumSqrD = 0.0; |
| double fVal1, fVal2; |
| for (i = 0; i < nC1; i++) |
| for (j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| fVal1 = pMat1->GetDouble(i,j); |
| fVal2 = pMat2->GetDouble(i,j); |
| fSum1 += fVal1; |
| fSum2 += fVal2; |
| fSumSqrD += (fVal1 - fVal2)*(fVal1 - fVal2); |
| fCount++; |
| } |
| } |
| if (fCount < 1.0) |
| { |
| PushNoValue(); |
| return; |
| } |
| fT = sqrt(fCount-1.0) * fabs(fSum1 - fSum2) / |
| sqrt(fCount * fSumSqrD - (fSum1-fSum2)*(fSum1-fSum2)); |
| fF = fCount - 1.0; |
| } |
| else if (fTyp == 2.0) |
| { |
| CalculateTest(sal_False,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF); |
| } |
| else if (fTyp == 3.0) |
| { |
| CalculateTest(sal_True,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF); |
| } |
| |
| else |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| if (fAnz == 1.0) |
| PushDouble(GetTDist(fT, fF)); |
| else |
| PushDouble(2.0*GetTDist(fT, fF)); |
| } |
| |
| void ScInterpreter::ScFTest() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFTest" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| ScMatrixRef pMat2 = GetMatrix(); |
| ScMatrixRef pMat1 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| SCSIZE i, j; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| double fCount1 = 0.0; |
| double fCount2 = 0.0; |
| double fSum1 = 0.0; |
| double fSumSqr1 = 0.0; |
| double fSum2 = 0.0; |
| double fSumSqr2 = 0.0; |
| double fVal; |
| for (i = 0; i < nC1; i++) |
| for (j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j)) |
| { |
| fVal = pMat1->GetDouble(i,j); |
| fSum1 += fVal; |
| fSumSqr1 += fVal * fVal; |
| fCount1++; |
| } |
| } |
| for (i = 0; i < nC2; i++) |
| for (j = 0; j < nR2; j++) |
| { |
| if (!pMat2->IsString(i,j)) |
| { |
| fVal = pMat2->GetDouble(i,j); |
| fSum2 += fVal; |
| fSumSqr2 += fVal * fVal; |
| fCount2++; |
| } |
| } |
| if (fCount1 < 2.0 || fCount2 < 2.0) |
| { |
| PushNoValue(); |
| return; |
| } |
| double fS1 = (fSumSqr1-fSum1*fSum1/fCount1)/(fCount1-1.0); |
| double fS2 = (fSumSqr2-fSum2*fSum2/fCount2)/(fCount2-1.0); |
| if (fS1 == 0.0 || fS2 == 0.0) |
| { |
| PushNoValue(); |
| return; |
| } |
| double fF, fF1, fF2; |
| if (fS1 > fS2) |
| { |
| fF = fS1/fS2; |
| fF1 = fCount1-1.0; |
| fF2 = fCount2-1.0; |
| } |
| else |
| { |
| fF = fS2/fS1; |
| fF1 = fCount2-1.0; |
| fF2 = fCount1-1.0; |
| } |
| PushDouble(2.0*GetFDist(fF, fF1, fF2)); |
| /* |
| double Z = (pow(fF,1.0/3.0)*(1.0-2.0/(9.0*fF2)) - (1.0-2.0/(9.0*fF1))) / |
| sqrt(2.0/(9.0*fF1) + pow(fF,2.0/3.0)*2.0/(9.0*fF2)); |
| PushDouble(1.0-2.0*gauss(Z)); |
| */ |
| } |
| |
| void ScInterpreter::ScChiTest() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiTest" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| ScMatrixRef pMat2 = GetMatrix(); |
| ScMatrixRef pMat1 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| if (nR1 != nR2 || nC1 != nC2) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| double fChi = 0.0; |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValE = pMat2->GetDouble(i,j); |
| fChi += (fValX - fValE) * (fValX - fValE) / fValE; |
| } |
| else |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| } |
| } |
| double fDF; |
| if (nC1 == 1 || nR1 == 1) |
| { |
| fDF = (double)(nC1*nR1 - 1); |
| if (fDF == 0.0) |
| { |
| PushNoValue(); |
| return; |
| } |
| } |
| else |
| fDF = (double)(nC1-1)*(double)(nR1-1); |
| PushDouble(GetChiDist(fChi, fDF)); |
| /* |
| double fX, fS, fT, fG; |
| fX = 1.0; |
| for (double fi = fDF; fi >= 2.0; fi -= 2.0) |
| fX *= fChi/fi; |
| fX *= exp(-fChi/2.0); |
| if (fmod(fDF, 2.0) != 0.0) |
| fX *= sqrt(2.0*fChi/F_PI); |
| fS = 1.0; |
| fT = 1.0; |
| fG = fDF; |
| while (fT >= 1.0E-7) |
| { |
| fG += 2.0; |
| fT *= fChi/fG; |
| fS += fT; |
| } |
| PushDouble(1.0 - fX*fS); |
| */ |
| } |
| |
| void ScInterpreter::ScKurt() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKurt" ); |
| double fSum,fCount,vSum; |
| std::vector<double> values; |
| if ( !CalculateSkew(fSum,fCount,vSum,values) ) |
| return; |
| |
| if (fCount == 0.0) |
| { |
| PushError( errDivisionByZero); |
| return; |
| } |
| |
| double fMean = fSum / fCount; |
| |
| for (size_t i = 0; i < values.size(); i++) |
| vSum += (values[i] - fMean) * (values[i] - fMean); |
| |
| double fStdDev = sqrt(vSum / (fCount - 1.0)); |
| double dx = 0.0; |
| double xpower4 = 0.0; |
| |
| if (fStdDev == 0.0) |
| { |
| PushError( errDivisionByZero); |
| return; |
| } |
| |
| for (size_t i = 0; i < values.size(); i++) |
| { |
| dx = (values[i] - fMean) / fStdDev; |
| xpower4 = xpower4 + (dx * dx * dx * dx); |
| } |
| |
| double k_d = (fCount - 2.0) * (fCount - 3.0); |
| double k_l = fCount * (fCount + 1.0) / ((fCount - 1.0) * k_d); |
| double k_t = 3.0 * (fCount - 1.0) * (fCount - 1.0) / k_d; |
| |
| PushDouble(xpower4 * k_l - k_t); |
| } |
| |
| void ScInterpreter::ScHarMean() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHarMean" ); |
| short nParamCount = GetByte(); |
| double nVal = 0.0; |
| double nValCount = 0.0; |
| ScAddress aAdr; |
| ScRange aRange; |
| size_t nRefInList = 0; |
| while ((nGlobalError == 0) && (nParamCount-- > 0)) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| { |
| double x = GetDouble(); |
| if (x > 0.0) |
| { |
| nVal += 1.0/x; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| break; |
| } |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| double x = GetCellValue( aAdr, pCell ); |
| if (x > 0.0) |
| { |
| nVal += 1.0/x; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| break; |
| } |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| sal_uInt16 nErr = 0; |
| PopDoubleRef( aRange, nParamCount, nRefInList); |
| double nCellVal; |
| ScValueIterator aValIter(pDok, aRange, glSubTotal); |
| if (aValIter.GetFirst(nCellVal, nErr)) |
| { |
| if (nCellVal > 0.0) |
| { |
| nVal += 1.0/nCellVal; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) |
| { |
| if (nCellVal > 0.0) |
| { |
| nVal += 1.0/nCellVal; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| { |
| double x = pMat->GetDouble(nElem); |
| if (x > 0.0) |
| { |
| nVal += 1.0/x; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| } |
| else |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| if (!pMat->IsString(nElem)) |
| { |
| double x = pMat->GetDouble(nElem); |
| if (x > 0.0) |
| { |
| nVal += 1.0/x; |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| } |
| } |
| } |
| break; |
| default : SetError(errIllegalParameter); break; |
| } |
| } |
| if (nGlobalError == 0) |
| PushDouble((double)nValCount/nVal); |
| else |
| PushError( nGlobalError); |
| } |
| |
| void ScInterpreter::ScGeoMean() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGeoMean" ); |
| short nParamCount = GetByte(); |
| double nVal = 0.0; |
| double nValCount = 0.0; |
| ScAddress aAdr; |
| ScRange aRange; |
| |
| size_t nRefInList = 0; |
| while ((nGlobalError == 0) && (nParamCount-- > 0)) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| { |
| double x = GetDouble(); |
| if (x > 0.0) |
| { |
| nVal += log(x); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| break; |
| } |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| double x = GetCellValue( aAdr, pCell ); |
| if (x > 0.0) |
| { |
| nVal += log(x); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| break; |
| } |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| sal_uInt16 nErr = 0; |
| PopDoubleRef( aRange, nParamCount, nRefInList); |
| double nCellVal; |
| ScValueIterator aValIter(pDok, aRange, glSubTotal); |
| if (aValIter.GetFirst(nCellVal, nErr)) |
| { |
| if (nCellVal > 0.0) |
| { |
| nVal += log(nCellVal); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) |
| { |
| if (nCellVal > 0.0) |
| { |
| nVal += log(nCellVal); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE ui = 0; ui < nCount; ui++) |
| { |
| double x = pMat->GetDouble(ui); |
| if (x > 0.0) |
| { |
| nVal += log(x); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| } |
| else |
| { |
| for (SCSIZE ui = 0; ui < nCount; ui++) |
| if (!pMat->IsString(ui)) |
| { |
| double x = pMat->GetDouble(ui); |
| if (x > 0.0) |
| { |
| nVal += log(x); |
| nValCount++; |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| } |
| } |
| } |
| break; |
| default : SetError(errIllegalParameter); break; |
| } |
| } |
| if (nGlobalError == 0) |
| PushDouble(exp(nVal / nValCount)); |
| else |
| PushError( nGlobalError); |
| } |
| |
| void ScInterpreter::ScStandard() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStandard" ); |
| if ( MustHaveParamCount( GetByte(), 3 ) ) |
| { |
| double sigma = GetDouble(); |
| double mue = GetDouble(); |
| double x = GetDouble(); |
| if (sigma < 0.0) |
| PushError( errIllegalArgument); |
| else if (sigma == 0.0) |
| PushError( errDivisionByZero); |
| else |
| PushDouble((x-mue)/sigma); |
| } |
| } |
| bool ScInterpreter::CalculateSkew(double& fSum,double& fCount,double& vSum,std::vector<double>& values) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSkew" ); |
| short nParamCount = GetByte(); |
| if ( !MustHaveParamCountMin( nParamCount, 1 ) ) |
| return false; |
| |
| fSum = 0.0; |
| fCount = 0.0; |
| vSum = 0.0; |
| double fVal = 0.0; |
| ScAddress aAdr; |
| ScRange aRange; |
| size_t nRefInList = 0; |
| while (nParamCount-- > 0) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| { |
| fVal = GetDouble(); |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| } |
| break; |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| fVal = GetCellValue( aAdr, pCell ); |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| } |
| } |
| break; |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| PopDoubleRef( aRange, nParamCount, nRefInList); |
| sal_uInt16 nErr = 0; |
| ScValueIterator aValIter(pDok, aRange); |
| if (aValIter.GetFirst(fVal, nErr)) |
| { |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext(fVal, nErr)) |
| { |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| } |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| { |
| fVal = pMat->GetDouble(nElem); |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| } |
| } |
| else |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| if (!pMat->IsString(nElem)) |
| { |
| fVal = pMat->GetDouble(nElem); |
| fSum += fVal; |
| values.push_back(fVal); |
| fCount++; |
| } |
| } |
| } |
| } |
| break; |
| default : |
| SetError(errIllegalParameter); |
| break; |
| } |
| } |
| |
| if (nGlobalError) |
| { |
| PushError( nGlobalError); |
| return false; |
| } // if (nGlobalError) |
| return true; |
| } |
| |
| void ScInterpreter::ScSkew() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSkew" ); |
| double fSum,fCount,vSum; |
| std::vector<double> values; |
| if ( !CalculateSkew(fSum,fCount,vSum,values) ) |
| return; |
| |
| double fMean = fSum / fCount; |
| |
| for (size_t i = 0; i < values.size(); i++) |
| vSum += (values[i] - fMean) * (values[i] - fMean); |
| |
| double fStdDev = sqrt(vSum / (fCount - 1.0)); |
| double dx = 0.0; |
| double xcube = 0.0; |
| |
| if (fStdDev == 0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| |
| for (size_t i = 0; i < values.size(); i++) |
| { |
| dx = (values[i] - fMean) / fStdDev; |
| xcube = xcube + (dx * dx * dx); |
| } |
| |
| PushDouble(((xcube * fCount) / (fCount - 1.0)) / (fCount - 2.0)); |
| } |
| |
| double ScInterpreter::GetMedian( vector<double> & rArray ) |
| { |
| size_t nSize = rArray.size(); |
| if (rArray.empty() || nSize == 0 || nGlobalError) |
| { |
| SetError( errNoValue); |
| return 0.0; |
| } |
| |
| // Upper median. |
| size_t nMid = nSize / 2; |
| vector<double>::iterator iMid = rArray.begin() + nMid; |
| ::std::nth_element( rArray.begin(), iMid, rArray.end()); |
| if (nSize & 1) |
| return *iMid; // Lower and upper median are equal. |
| else |
| { |
| double fUp = *iMid; |
| // Lower median. |
| iMid = rArray.begin() + nMid - 1; |
| ::std::nth_element( rArray.begin(), iMid, rArray.end()); |
| return (fUp + *iMid) / 2; |
| } |
| } |
| |
| void ScInterpreter::ScMedian() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMedian" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCountMin( nParamCount, 1 ) ) |
| return; |
| vector<double> aArray; |
| GetNumberSequenceArray( nParamCount, aArray); |
| PushDouble( GetMedian( aArray)); |
| } |
| |
| double ScInterpreter::GetPercentile( vector<double> & rArray, double fPercentile ) |
| { |
| size_t nSize = rArray.size(); |
| if (rArray.empty() || nSize == 0 || nGlobalError) |
| { |
| SetError( errNoValue); |
| return 0.0; |
| } |
| |
| if (nSize == 1) |
| return rArray[0]; |
| else |
| { |
| size_t nIndex = (size_t)::rtl::math::approxFloor( fPercentile * (nSize-1)); |
| double fDiff = fPercentile * (nSize-1) - ::rtl::math::approxFloor( fPercentile * (nSize-1)); |
| DBG_ASSERT(nIndex < nSize, "GetPercentile: wrong index(1)"); |
| vector<double>::iterator iter = rArray.begin() + nIndex; |
| ::std::nth_element( rArray.begin(), iter, rArray.end()); |
| if (fDiff == 0.0) |
| return *iter; |
| else |
| { |
| DBG_ASSERT(nIndex < nSize-1, "GetPercentile: wrong index(2)"); |
| double fVal = *iter; |
| iter = rArray.begin() + nIndex+1; |
| ::std::nth_element( rArray.begin(), iter, rArray.end()); |
| return fVal + fDiff * (*iter - fVal); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScPercentile() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentile" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double alpha = GetDouble(); |
| if (alpha < 0.0 || alpha > 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| vector<double> aArray; |
| GetNumberSequenceArray( 1, aArray); |
| PushDouble( GetPercentile( aArray, alpha)); |
| } |
| |
| void ScInterpreter::ScQuartile() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScQuartile" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double fFlag = ::rtl::math::approxFloor(GetDouble()); |
| if (fFlag < 0.0 || fFlag > 4.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| vector<double> aArray; |
| GetNumberSequenceArray( 1, aArray); |
| PushDouble( fFlag == 2.0 ? GetMedian( aArray) : GetPercentile( aArray, 0.25 * fFlag)); |
| } |
| |
| void ScInterpreter::ScModalValue() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScModalValue" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCountMin( nParamCount, 1 ) ) |
| return; |
| vector<double> aSortArray; |
| GetSortArray(nParamCount, aSortArray); |
| SCSIZE nSize = aSortArray.size(); |
| if (aSortArray.empty() || nSize == 0 || nGlobalError) |
| PushNoValue(); |
| else |
| { |
| SCSIZE nMaxIndex = 0, nMax = 1, nCount = 1; |
| double nOldVal = aSortArray[0]; |
| SCSIZE i; |
| |
| for ( i = 1; i < nSize; i++) |
| { |
| if (aSortArray[i] == nOldVal) |
| nCount++; |
| else |
| { |
| nOldVal = aSortArray[i]; |
| if (nCount > nMax) |
| { |
| nMax = nCount; |
| nMaxIndex = i-1; |
| } |
| nCount = 1; |
| } |
| } |
| if (nCount > nMax) |
| { |
| nMax = nCount; |
| nMaxIndex = i-1; |
| } |
| if (nMax == 1 && nCount == 1) |
| PushNoValue(); |
| else if (nMax == 1) |
| PushDouble(nOldVal); |
| else |
| PushDouble(aSortArray[nMaxIndex]); |
| } |
| } |
| |
| void ScInterpreter::CalculateSmallLarge(sal_Bool bSmall) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSmallLarge" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double f = ::rtl::math::approxFloor(GetDouble()); |
| if (f < 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| SCSIZE k = static_cast<SCSIZE>(f); |
| vector<double> aSortArray; |
| /* TODO: using nth_element() is best for one single value, but LARGE/SMALL |
| * actually are defined to return an array of values if an array of |
| * positions was passed, in which case, depending on the number of values, |
| * we may or will need a real sorted array again, see #i32345. */ |
| //GetSortArray(1, aSortArray); |
| GetNumberSequenceArray(1, aSortArray); |
| SCSIZE nSize = aSortArray.size(); |
| if (aSortArray.empty() || nSize == 0 || nGlobalError || nSize < k) |
| PushNoValue(); |
| else |
| { |
| // TODO: the sorted case for array: PushDouble( aSortArray[ bSmall ? k-1 : nSize-k ] ); |
| vector<double>::iterator iPos = aSortArray.begin() + (bSmall ? k-1 : nSize-k); |
| ::std::nth_element( aSortArray.begin(), iPos, aSortArray.end()); |
| PushDouble( *iPos); |
| } |
| } |
| |
| void ScInterpreter::ScLarge() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLarge" ); |
| CalculateSmallLarge(sal_False); |
| } |
| |
| void ScInterpreter::ScSmall() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSmall" ); |
| CalculateSmallLarge(sal_True); |
| } |
| |
| void ScInterpreter::ScPercentrank() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentrank" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 2 ) ) |
| return; |
| #if 0 |
| /* wird nicht unterstuetzt |
| double fPrec; |
| if (nParamCount == 3) |
| { |
| fPrec = ::rtl::math::approxFloor(GetDouble()); |
| if (fPrec < 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| } |
| else |
| fPrec = 3.0; |
| */ |
| #endif |
| double fNum = GetDouble(); |
| vector<double> aSortArray; |
| GetSortArray(1, aSortArray); |
| SCSIZE nSize = aSortArray.size(); |
| if (aSortArray.empty() || nSize == 0 || nGlobalError) |
| PushNoValue(); |
| else |
| { |
| if (fNum < aSortArray[0] || fNum > aSortArray[nSize-1]) |
| PushNoValue(); |
| else if ( nSize == 1 ) |
| PushDouble(1.0); // fNum == pSortArray[0], see test above |
| else |
| { |
| double fRes; |
| SCSIZE nOldCount = 0; |
| double fOldVal = aSortArray[0]; |
| SCSIZE i; |
| for (i = 1; i < nSize && aSortArray[i] < fNum; i++) |
| { |
| if (aSortArray[i] != fOldVal) |
| { |
| nOldCount = i; |
| fOldVal = aSortArray[i]; |
| } |
| } |
| if (aSortArray[i] != fOldVal) |
| nOldCount = i; |
| if (fNum == aSortArray[i]) |
| fRes = (double)nOldCount/(double)(nSize-1); |
| else |
| { |
| // #75312# nOldCount is the count of smaller entries |
| // fNum is between pSortArray[nOldCount-1] and pSortArray[nOldCount] |
| // use linear interpolation to find a position between the entries |
| |
| if ( nOldCount == 0 ) |
| { |
| DBG_ERROR("should not happen"); |
| fRes = 0.0; |
| } |
| else |
| { |
| double fFract = ( fNum - aSortArray[nOldCount-1] ) / |
| ( aSortArray[nOldCount] - aSortArray[nOldCount-1] ); |
| fRes = ( (double)(nOldCount-1)+fFract )/(double)(nSize-1); |
| } |
| } |
| PushDouble(fRes); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScTrimMean() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrimMean" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| double alpha = GetDouble(); |
| if (alpha < 0.0 || alpha >= 1.0) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| vector<double> aSortArray; |
| GetSortArray(1, aSortArray); |
| SCSIZE nSize = aSortArray.size(); |
| if (aSortArray.empty() || nSize == 0 || nGlobalError) |
| PushNoValue(); |
| else |
| { |
| sal_uLong nIndex = (sal_uLong) ::rtl::math::approxFloor(alpha*(double)nSize); |
| if (nIndex % 2 != 0) |
| nIndex--; |
| nIndex /= 2; |
| DBG_ASSERT(nIndex < nSize, "ScTrimMean: falscher Index"); |
| double fSum = 0.0; |
| for (SCSIZE i = nIndex; i < nSize-nIndex; i++) |
| fSum += aSortArray[i]; |
| PushDouble(fSum/(double)(nSize-2*nIndex)); |
| } |
| } |
| |
| void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double>& rArray ) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetSortArray" ); |
| ScAddress aAdr; |
| ScRange aRange; |
| short nParam = nParamCount; |
| size_t nRefInList = 0; |
| while (nParam-- > 0) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| rArray.push_back( PopDouble()); |
| break; |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| rArray.push_back( GetCellValue( aAdr, pCell)); |
| } |
| break; |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| PopDoubleRef( aRange, nParam, nRefInList); |
| if (nGlobalError) |
| break; |
| |
| aRange.Justify(); |
| SCSIZE nCellCount = aRange.aEnd.Col() - aRange.aStart.Col() + 1; |
| nCellCount *= aRange.aEnd.Row() - aRange.aStart.Row() + 1; |
| rArray.reserve( rArray.size() + nCellCount); |
| |
| sal_uInt16 nErr = 0; |
| double fCellVal; |
| ScValueIterator aValIter(pDok, aRange); |
| if (aValIter.GetFirst( fCellVal, nErr)) |
| { |
| rArray.push_back( fCellVal); |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext( fCellVal, nErr)) |
| rArray.push_back( fCellVal); |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (!pMat) |
| break; |
| |
| SCSIZE nCount = pMat->GetElementCount(); |
| rArray.reserve( rArray.size() + nCount); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE i = 0; i < nCount; ++i) |
| rArray.push_back( pMat->GetDouble(i)); |
| } |
| else |
| { |
| for (SCSIZE i = 0; i < nCount; ++i) |
| if (!pMat->IsString(i)) |
| rArray.push_back( pMat->GetDouble(i)); |
| } |
| } |
| break; |
| default : |
| PopError(); |
| SetError( errIllegalParameter); |
| break; |
| } |
| if (nGlobalError) |
| break; // while |
| } |
| // nParam > 0 in case of error, clean stack environment and obtain earlier |
| // error if there was one. |
| while (nParam-- > 0) |
| PopError(); |
| } |
| |
| void ScInterpreter::GetSortArray( sal_uInt8 nParamCount, vector<double>& rSortArray, vector<long>* pIndexOrder ) |
| { |
| GetNumberSequenceArray( nParamCount, rSortArray); |
| |
| if (rSortArray.size() > MAX_ANZ_DOUBLE_FOR_SORT) |
| SetError( errStackOverflow); |
| else if (rSortArray.empty()) |
| SetError( errNoValue); |
| |
| if (nGlobalError == 0) |
| QuickSort( rSortArray, pIndexOrder); |
| } |
| |
| static void lcl_QuickSort( long nLo, long nHi, vector<double>& rSortArray, vector<long>* pIndexOrder ) |
| { |
| // If pIndexOrder is not NULL, we assume rSortArray.size() == pIndexOrder->size(). |
| |
| using ::std::swap; |
| |
| if (nHi - nLo == 1) |
| { |
| if (rSortArray[nLo] > rSortArray[nHi]) |
| { |
| swap(rSortArray[nLo], rSortArray[nHi]); |
| if (pIndexOrder) |
| swap(pIndexOrder->at(nLo), pIndexOrder->at(nHi)); |
| } |
| return; |
| } |
| |
| long ni = nLo; |
| long nj = nHi; |
| do |
| { |
| double fLo = rSortArray[nLo]; |
| while (ni <= nHi && rSortArray[ni] < fLo) ni++; |
| while (nj >= nLo && fLo < rSortArray[nj]) nj--; |
| if (ni <= nj) |
| { |
| if (ni != nj) |
| { |
| swap(rSortArray[ni], rSortArray[nj]); |
| if (pIndexOrder) |
| swap(pIndexOrder->at(ni), pIndexOrder->at(nj)); |
| } |
| |
| ++ni; |
| --nj; |
| } |
| } |
| while (ni < nj); |
| |
| if ((nj - nLo) < (nHi - ni)) |
| { |
| if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder); |
| if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder); |
| } |
| else |
| { |
| if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder); |
| if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder); |
| } |
| } |
| |
| void ScInterpreter::QuickSort( vector<double>& rSortArray, vector<long>* pIndexOrder ) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::QuickSort" ); |
| long n = static_cast<long>(rSortArray.size()); |
| |
| if (pIndexOrder) |
| { |
| pIndexOrder->clear(); |
| pIndexOrder->reserve(n); |
| for (long i = 0; i < n; ++i) |
| pIndexOrder->push_back(i); |
| } |
| |
| if (n < 2) |
| return; |
| |
| size_t nValCount = rSortArray.size(); |
| for (size_t i = 0; (i + 4) <= nValCount-1; i += 4) |
| { |
| size_t nInd = rand() % (int) (nValCount-1); |
| ::std::swap( rSortArray[i], rSortArray[nInd]); |
| if (pIndexOrder) |
| ::std::swap( pIndexOrder->at(i), pIndexOrder->at(nInd)); |
| } |
| |
| lcl_QuickSort(0, n-1, rSortArray, pIndexOrder); |
| } |
| |
| void ScInterpreter::ScRank() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRank" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) |
| return; |
| sal_Bool bDescending; |
| if (nParamCount == 3) |
| bDescending = GetBool(); |
| else |
| bDescending = sal_False; |
| double fCount = 1.0; |
| sal_Bool bValid = sal_False; |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| { |
| double x = GetDouble(); |
| double fVal = GetDouble(); |
| if (x == fVal) |
| bValid = sal_True; |
| break; |
| } |
| case svSingleRef : |
| { |
| ScAddress aAdr; |
| PopSingleRef( aAdr ); |
| double fVal = GetDouble(); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| double x = GetCellValue( aAdr, pCell ); |
| if (x == fVal) |
| bValid = sal_True; |
| } |
| break; |
| } |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| ScRange aRange; |
| short nParam = 1; |
| size_t nRefInList = 0; |
| while (nParam-- > 0) |
| { |
| sal_uInt16 nErr = 0; |
| // Preserve stack until all RefList elements are done! |
| sal_uInt16 nSaveSP = sp; |
| PopDoubleRef( aRange, nParam, nRefInList); |
| if (nParam) |
| --sp; // simulate pop |
| double fVal = GetDouble(); |
| if (nParam) |
| sp = nSaveSP; |
| double nCellVal; |
| ScValueIterator aValIter(pDok, aRange, glSubTotal); |
| if (aValIter.GetFirst(nCellVal, nErr)) |
| { |
| if (nCellVal == fVal) |
| bValid = sal_True; |
| else if ((!bDescending && nCellVal > fVal) || |
| (bDescending && nCellVal < fVal) ) |
| fCount++; |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) |
| { |
| if (nCellVal == fVal) |
| bValid = sal_True; |
| else if ((!bDescending && nCellVal > fVal) || |
| (bDescending && nCellVal < fVal) ) |
| fCount++; |
| } |
| } |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| double fVal = GetDouble(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE i = 0; i < nCount; i++) |
| { |
| double x = pMat->GetDouble(i); |
| if (x == fVal) |
| bValid = sal_True; |
| else if ((!bDescending && x > fVal) || |
| (bDescending && x < fVal) ) |
| fCount++; |
| } |
| } |
| else |
| { |
| for (SCSIZE i = 0; i < nCount; i++) |
| if (!pMat->IsString(i)) |
| { |
| double x = pMat->GetDouble(i); |
| if (x == fVal) |
| bValid = sal_True; |
| else if ((!bDescending && x > fVal) || |
| (bDescending && x < fVal) ) |
| fCount++; |
| } |
| } |
| } |
| } |
| break; |
| default : SetError(errIllegalParameter); break; |
| } |
| if (bValid) |
| PushDouble(fCount); |
| else |
| PushNoValue(); |
| } |
| |
| void ScInterpreter::ScAveDev() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAveDev" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCountMin( nParamCount, 1 ) ) |
| return; |
| sal_uInt16 SaveSP = sp; |
| double nMiddle = 0.0; |
| double rVal = 0.0; |
| double rValCount = 0.0; |
| ScAddress aAdr; |
| ScRange aRange; |
| short nParam = nParamCount; |
| size_t nRefInList = 0; |
| while (nParam-- > 0) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| rVal += GetDouble(); |
| rValCount++; |
| break; |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| { |
| rVal += GetCellValue( aAdr, pCell ); |
| rValCount++; |
| } |
| } |
| break; |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| sal_uInt16 nErr = 0; |
| double nCellVal; |
| PopDoubleRef( aRange, nParam, nRefInList); |
| ScValueIterator aValIter(pDok, aRange); |
| if (aValIter.GetFirst(nCellVal, nErr)) |
| { |
| rVal += nCellVal; |
| rValCount++; |
| SetError(nErr); |
| while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) |
| { |
| rVal += nCellVal; |
| rValCount++; |
| } |
| SetError(nErr); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| { |
| rVal += pMat->GetDouble(nElem); |
| rValCount++; |
| } |
| } |
| else |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| if (!pMat->IsString(nElem)) |
| { |
| rVal += pMat->GetDouble(nElem); |
| rValCount++; |
| } |
| } |
| } |
| } |
| break; |
| default : |
| SetError(errIllegalParameter); |
| break; |
| } |
| } |
| if (nGlobalError) |
| { |
| PushError( nGlobalError); |
| return; |
| } |
| nMiddle = rVal / rValCount; |
| sp = SaveSP; |
| rVal = 0.0; |
| nParam = nParamCount; |
| nRefInList = 0; |
| while (nParam-- > 0) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble : |
| rVal += fabs(GetDouble() - nMiddle); |
| break; |
| case svSingleRef : |
| { |
| PopSingleRef( aAdr ); |
| ScBaseCell* pCell = GetCell( aAdr ); |
| if (HasCellValueData(pCell)) |
| rVal += fabs(GetCellValue( aAdr, pCell ) - nMiddle); |
| } |
| break; |
| case formula::svDoubleRef : |
| case svRefList : |
| { |
| sal_uInt16 nErr = 0; |
| double nCellVal; |
| PopDoubleRef( aRange, nParam, nRefInList); |
| ScValueIterator aValIter(pDok, aRange); |
| if (aValIter.GetFirst(nCellVal, nErr)) |
| { |
| rVal += (fabs(nCellVal - nMiddle)); |
| while (aValIter.GetNext(nCellVal, nErr)) |
| rVal += fabs(nCellVal - nMiddle); |
| } |
| } |
| break; |
| case svMatrix : |
| { |
| ScMatrixRef pMat = PopMatrix(); |
| if (pMat) |
| { |
| SCSIZE nCount = pMat->GetElementCount(); |
| if (pMat->IsNumeric()) |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| { |
| rVal += fabs(pMat->GetDouble(nElem) - nMiddle); |
| } |
| } |
| else |
| { |
| for (SCSIZE nElem = 0; nElem < nCount; nElem++) |
| { |
| if (!pMat->IsString(nElem)) |
| rVal += fabs(pMat->GetDouble(nElem) - nMiddle); |
| } |
| } |
| } |
| } |
| break; |
| default : SetError(errIllegalParameter); break; |
| } |
| } |
| PushDouble(rVal / rValCount); |
| } |
| |
| void ScInterpreter::ScDevSq() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDevSq" ); |
| double nVal; |
| double nValCount; |
| GetStVarParams(nVal, nValCount); |
| PushDouble(nVal); |
| } |
| |
| void ScInterpreter::ScProbability() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScProbability" ); |
| sal_uInt8 nParamCount = GetByte(); |
| if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) |
| return; |
| double fUp, fLo; |
| fUp = GetDouble(); |
| if (nParamCount == 4) |
| fLo = GetDouble(); |
| else |
| fLo = fUp; |
| if (fLo > fUp) |
| { |
| double fTemp = fLo; |
| fLo = fUp; |
| fUp = fTemp; |
| } |
| ScMatrixRef pMatP = GetMatrix(); |
| ScMatrixRef pMatW = GetMatrix(); |
| if (!pMatP || !pMatW) |
| PushIllegalParameter(); |
| else |
| { |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| pMatP->GetDimensions(nC1, nR1); |
| pMatW->GetDimensions(nC2, nR2); |
| if (nC1 != nC2 || nR1 != nR2 || nC1 == 0 || nR1 == 0 || |
| nC2 == 0 || nR2 == 0) |
| PushNA(); |
| else |
| { |
| double fSum = 0.0; |
| double fRes = 0.0; |
| sal_Bool bStop = sal_False; |
| double fP, fW; |
| SCSIZE nCount1 = nC1 * nR1; |
| for ( SCSIZE i = 0; i < nCount1 && !bStop; i++ ) |
| { |
| if (pMatP->IsValue(i) && pMatW->IsValue(i)) |
| { |
| fP = pMatP->GetDouble(i); |
| fW = pMatW->GetDouble(i); |
| if (fP < 0.0 || fP > 1.0) |
| bStop = sal_True; |
| else |
| { |
| fSum += fP; |
| if (fW >= fLo && fW <= fUp) |
| fRes += fP; |
| } |
| } |
| else |
| SetError( errIllegalArgument); |
| } |
| if (bStop || fabs(fSum -1.0) > 1.0E-7) |
| PushNoValue(); |
| else |
| PushDouble(fRes); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScCorrel() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCorrel" ); |
| // This is identical to ScPearson() |
| ScPearson(); |
| } |
| |
| void ScInterpreter::ScCovar() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCovar" ); |
| CalculatePearsonCovar(sal_False,sal_False); |
| } |
| |
| void ScInterpreter::ScPearson() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPearson" ); |
| CalculatePearsonCovar(sal_True,sal_False); |
| } |
| void ScInterpreter::CalculatePearsonCovar(sal_Bool _bPearson,sal_Bool _bStexy) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculatePearsonCovar" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| ScMatrixRef pMat1 = GetMatrix(); |
| ScMatrixRef pMat2 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| if (nR1 != nR2 || nC1 != nC2) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| /* #i78250# |
| * (sum((X-MeanX)(Y-MeanY)))/N equals (SumXY)/N-MeanX*MeanY mathematically, |
| * but the latter produces wrong results if the absolute values are high, |
| * for example above 10^8 |
| */ |
| double fCount = 0.0; |
| double fSumX = 0.0; |
| double fSumY = 0.0; |
| double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) |
| double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 |
| double fSumSqrDeltaY = 0.0; // sum of (ValY-MeanY)^2 |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValY = pMat2->GetDouble(i,j); |
| fSumX += fValX; |
| fSumY += fValY; |
| fCount++; |
| } |
| } |
| } |
| if (fCount < (_bStexy ? 3.0 : 1.0)) // fCount==1 is handled by checking denominator later on |
| PushNoValue(); |
| else |
| { |
| const double fMeanX = fSumX / fCount; |
| const double fMeanY = fSumY / fCount; |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| const double fValX = pMat1->GetDouble(i,j); |
| const double fValY = pMat2->GetDouble(i,j); |
| fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); |
| if ( _bPearson ) |
| { |
| fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); |
| fSumSqrDeltaY += (fValY - fMeanY) * (fValY - fMeanY); |
| } |
| } |
| } |
| } // for (SCSIZE i = 0; i < nC1; i++) |
| if ( _bPearson ) |
| { |
| if (fSumSqrDeltaX == 0.0 || ( !_bStexy && fSumSqrDeltaY == 0.0) ) |
| PushError( errDivisionByZero); |
| else if ( _bStexy ) |
| PushDouble( sqrt( (fSumSqrDeltaY - fSumDeltaXDeltaY * |
| fSumDeltaXDeltaY / fSumSqrDeltaX) / (fCount-2))); |
| else |
| PushDouble( fSumDeltaXDeltaY / sqrt( fSumSqrDeltaX * fSumSqrDeltaY)); |
| } // if ( _bPearson ) |
| else |
| { |
| PushDouble( fSumDeltaXDeltaY / fCount); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScRSQ() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRSQ" ); |
| // Same as ScPearson()*ScPearson() |
| ScPearson(); |
| if (!nGlobalError) |
| { |
| switch (GetStackType()) |
| { |
| case formula::svDouble: |
| { |
| double fVal = PopDouble(); |
| PushDouble( fVal * fVal); |
| } |
| break; |
| default: |
| PopError(); |
| PushNoValue(); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScSTEXY() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSTEXY" ); |
| CalculatePearsonCovar(sal_True,sal_True); |
| } |
| void ScInterpreter::CalculateSlopeIntercept(sal_Bool bSlope) |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSlopeIntercept" ); |
| if ( !MustHaveParamCount( GetByte(), 2 ) ) |
| return; |
| ScMatrixRef pMat1 = GetMatrix(); |
| ScMatrixRef pMat2 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| if (nR1 != nR2 || nC1 != nC2) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| // #i78250# numerical stability improved |
| double fCount = 0.0; |
| double fSumX = 0.0; |
| double fSumY = 0.0; |
| double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) |
| double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValY = pMat2->GetDouble(i,j); |
| fSumX += fValX; |
| fSumY += fValY; |
| fCount++; |
| } |
| } |
| } |
| if (fCount < 1.0) |
| PushNoValue(); |
| else |
| { |
| double fMeanX = fSumX / fCount; |
| double fMeanY = fSumY / fCount; |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValY = pMat2->GetDouble(i,j); |
| fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); |
| fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); |
| } |
| } |
| } |
| if (fSumSqrDeltaX == 0.0) |
| PushError( errDivisionByZero); |
| else |
| { |
| if ( bSlope ) |
| PushDouble( fSumDeltaXDeltaY / fSumSqrDeltaX); |
| else |
| PushDouble( fMeanY - fSumDeltaXDeltaY / fSumSqrDeltaX * fMeanX); |
| } |
| } |
| } |
| |
| void ScInterpreter::ScSlope() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSlope" ); |
| CalculateSlopeIntercept(sal_True); |
| } |
| |
| void ScInterpreter::ScIntercept() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIntercept" ); |
| CalculateSlopeIntercept(sal_False); |
| } |
| |
| void ScInterpreter::ScForecast() |
| { |
| RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScForecast" ); |
| if ( !MustHaveParamCount( GetByte(), 3 ) ) |
| return; |
| ScMatrixRef pMat1 = GetMatrix(); |
| ScMatrixRef pMat2 = GetMatrix(); |
| if (!pMat1 || !pMat2) |
| { |
| PushIllegalParameter(); |
| return; |
| } |
| SCSIZE nC1, nC2; |
| SCSIZE nR1, nR2; |
| pMat1->GetDimensions(nC1, nR1); |
| pMat2->GetDimensions(nC2, nR2); |
| if (nR1 != nR2 || nC1 != nC2) |
| { |
| PushIllegalArgument(); |
| return; |
| } |
| double fVal = GetDouble(); |
| // #i78250# numerical stability improved |
| double fCount = 0.0; |
| double fSumX = 0.0; |
| double fSumY = 0.0; |
| double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) |
| double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValY = pMat2->GetDouble(i,j); |
| fSumX += fValX; |
| fSumY += fValY; |
| fCount++; |
| } |
| } |
| } |
| if (fCount < 1.0) |
| PushNoValue(); |
| else |
| { |
| double fMeanX = fSumX / fCount; |
| double fMeanY = fSumY / fCount; |
| for (SCSIZE i = 0; i < nC1; i++) |
| { |
| for (SCSIZE j = 0; j < nR1; j++) |
| { |
| if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) |
| { |
| double fValX = pMat1->GetDouble(i,j); |
| double fValY = pMat2->GetDouble(i,j); |
| fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); |
| fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); |
| } |
| } |
| } |
| if (fSumSqrDeltaX == 0.0) |
| PushError( errDivisionByZero); |
| else |
| PushDouble( fMeanY + fSumDeltaXDeltaY / fSumSqrDeltaX * (fVal - fMeanX)); |
| } |
| } |
| |