Index: sc/source/core/inc/interpre.hxx =================================================================== RCS file: /cvs/sc/sc/source/core/inc/interpre.hxx,v retrieving revision 1.21 diff -u -r1.21 interpre.hxx --- sc/source/core/inc/interpre.hxx 13 May 2005 07:33:07 -0000 1.21 +++ sc/source/core/inc/interpre.hxx 9 Sep 2005 04:09:27 -0000 @@ -91,6 +91,7 @@ #endif #include +#include class ScDocument; class SbxVariable; @@ -623,6 +624,8 @@ double GetTDist(double T, double fDF); double Fakultaet(double x); double BinomKoeff(double n, double k); +BOOL SetBinomElements( double n, double k, + std::multiset& rEnum, std::multiset& rDenom, size_t nMaxSize ) const; double GammaHelp(double& x, BOOL& bReflect); double GetGamma(double x); double GetLogGamma(double x); Index: sc/source/core/tool/interpr3.cxx =================================================================== RCS file: /cvs/sc/sc/source/core/tool/interpr3.cxx,v retrieving revision 1.12 diff -u -r1.12 interpr3.cxx --- sc/source/core/tool/interpr3.cxx 15 Nov 2004 16:35:44 -0000 1.12 +++ sc/source/core/tool/interpr3.cxx 9 Sep 2005 04:09:27 -0000 @@ -345,6 +345,72 @@ return nVal; } +BOOL lcl_InsertElementIntoDenom( double k, + std::multiset& rEnum, std::multiset& rDenom, + size_t nMaxSize ) +{ + std::multiset::iterator itr = rEnum.find( k ); + if ( itr != rEnum.end() ) + rEnum.erase( itr ); + else + if ( rDenom.size() >= nMaxSize ) + return false; + else + rDenom.insert( k ); + return true; +} + +/** Inserts individual binomial elements of (n, k) = n! / k!(n-k)! into enumerator + and denominator arrays, respectively. + + @param n + @param k + @param rEnum multiset array to store enumerator elements + @param rDenom multiset array to store denominator elements + @param nMaxSize maximum allowable array size + + @return returns true if successful, else returns false. The caller is responsible + for flagging an appropriate error when it returns false. + + @author Kohei Yoshida + + @see #i47296# + */ +BOOL ScInterpreter::SetBinomElements( double n, double k, + std::multiset& rEnum, std::multiset& rDenom, + size_t nMaxSize ) const +{ + if ( n >= k ) + { + if ( n == 0.0 || k == 0.0 ) + return true; + if ( n > 1.0 ) + if ( rEnum.size() >= nMaxSize ) + return false; + else + rEnum.insert( n ); + if ( k > 1.0 ) + if ( !lcl_InsertElementIntoDenom( k, rEnum, rDenom, nMaxSize ) ) + return false; + --n; + --k; + while ( k > 0.0 ) + { + if ( n > 1.0 ) + if ( rEnum.size() >= nMaxSize ) + return false; + else + rEnum.insert( n ); + if ( k > 1.0 ) + if ( !lcl_InsertElementIntoDenom( k, rEnum, rDenom, nMaxSize ) ) + return false; + --n; + --k; + } + } + return true; +} + double ScInterpreter::GammaHelp(double& x, BOOL& bReflect) { double c[6] = {76.18009173, -86.50532033, 24.01409822, @@ -1115,36 +1181,50 @@ 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) ) { SetIllegalArgument(); return; } - double fFactor = - BinomKoeff( n, x ) / BinomKoeff( N, M ) * BinomKoeff( N - n, M - x ); -/* - double fFactor; - if (x == n - N + M) - fFactor = BinomKoeff(M,x)/BinomKoeff(N,n); - else + typedef ::std::multiset DoubleMultiSet; + DoubleMultiSet rEnum, rDenom; + + // max array size configurable up to max_size() (usually 4294967295). + // But larger the array becomes the slower the speed gets. The SetBinomElement() + // calls spend the majority of the execution time, so that's where a speedup + // may be needed. (see #i47296) + size_t nMaxArraySize = 50000; + if ( !SetBinomElements( n, x, rEnum, rDenom, nMaxArraySize ) ) { - double fIndex = N - M - n; - if (fIndex >= 0.0) - { - fFactor = BinomKoeff(N-M,n)/BinomKoeff(N,n); - for (double i = 0; i < x; i++) - fFactor *= (M-i)*(n-i)/((i+1.0)*(N-M-n+i+1.0)); - } - else - { - fFactor = BinomKoeff(M,-fIndex)/BinomKoeff(N,n); - for (double i = -fIndex + 1.0; i < x; i++) - fFactor *= (M-i)*(n-i)/((i+1)*(N-M-n+i+1.0)); - } + SetIllegalArgument(); + return; } -*/ + if ( !SetBinomElements( N, M, rDenom, rEnum, nMaxArraySize ) ) + { + SetIllegalArgument(); + return; + } + if ( !SetBinomElements( N-n, M-x, rEnum, rDenom, nMaxArraySize ) ) + { + SetIllegalArgument(); + return; + } + + double fFactor = 1.0; + DoubleMultiSet::reverse_iterator it1 = rEnum.rbegin(), it1End = rEnum.rend(); + DoubleMultiSet::reverse_iterator it2 = rDenom.rbegin(), it2End = rDenom.rend(); + 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); } }