diff options
Diffstat (limited to 'sc')
-rw-r--r-- | sc/qa/unit/data/functions/statistical/fods/large.fods | 8 | ||||
-rw-r--r-- | sc/qa/unit/data/functions/statistical/fods/small.fods | 16 | ||||
-rw-r--r-- | sc/source/core/tool/interpr3.cxx | 66 |
3 files changed, 72 insertions, 18 deletions
diff --git a/sc/qa/unit/data/functions/statistical/fods/large.fods b/sc/qa/unit/data/functions/statistical/fods/large.fods index a00fd8d522ce..6e701e71671f 100644 --- a/sc/qa/unit/data/functions/statistical/fods/large.fods +++ b/sc/qa/unit/data/functions/statistical/fods/large.fods @@ -4752,13 +4752,15 @@ <table:table-cell table:number-columns-repeated="2"/> </table:table-row> <table:table-row table:style-name="ro8"> - <table:table-cell table:style-name="ce11" table:formula="of:=AVERAGE(LARGE([.I29:.I38];{1;2;3}))" office:value-type="float" office:value="10" calcext:value-type="float"> - <text:p>10</text:p> + <table:table-cell table:style-name="ce11" table:formula="of:=AVERAGE(LARGE([.I29:.I38];{1;2;3}))" office:value-type="float" office:value="9" calcext:value-type="float"> + <text:p>9</text:p> </table:table-cell> <table:table-cell office:value-type="float" office:value="9" calcext:value-type="float"> <text:p>9</text:p> </table:table-cell> - <table:table-cell table:style-name="ce16"/> + <table:table-cell table:style-name="ce16" table:formula="of:=ROUND([.A29];12)=ROUND([.B29];12)" office:value-type="boolean" office:boolean-value="true" calcext:value-type="boolean"> + <text:p>TRUE</text:p> + </table:table-cell> <table:table-cell table:style-name="ce22" table:formula="of:=FORMULA([.A29])" office:value-type="string" office:string-value="=AVERAGE(LARGE(I29:I38,{1,2,3}))" calcext:value-type="string"> <text:p>=AVERAGE(LARGE(I29:I38,{1,2,3}))</text:p> </table:table-cell> diff --git a/sc/qa/unit/data/functions/statistical/fods/small.fods b/sc/qa/unit/data/functions/statistical/fods/small.fods index 5f191b43edb4..94a6ae6344ef 100644 --- a/sc/qa/unit/data/functions/statistical/fods/small.fods +++ b/sc/qa/unit/data/functions/statistical/fods/small.fods @@ -4155,12 +4155,18 @@ <table:table-cell table:number-columns-repeated="2"/> </table:table-row> <table:table-row table:style-name="ro6"> - <table:table-cell table:style-name="ce19" table:formula="of:=AVERAGE(SMALL([.F1:.F11];{5;2;3}))" office:value-type="float" office:value="4" calcext:value-type="float"> - <text:p>4</text:p> + <table:table-cell table:style-name="ce19" table:formula="of:=AVERAGE(SMALL([.F1:.F11];{5;2;3}))" office:value-type="float" office:value="1" calcext:value-type="float"> + <text:p>1</text:p> + </table:table-cell> + <table:table-cell office:value-type="float" office:value="1" calcext:value-type="float"> + <text:p>1</text:p> + </table:table-cell> + <table:table-cell table:style-name="ce25" table:formula="of:=ROUND([.A12];12)=ROUND([.B12];12)" office:value-type="boolean" office:boolean-value="true" calcext:value-type="boolean"> + <text:p>TRUE</text:p> + </table:table-cell> + <table:table-cell table:style-name="ce32" table:formula="of:=FORMULA([.A12])" office:value-type="string" office:string-value="=AVERAGE(SMALL(F1:F11,{5,2,3}))" calcext:value-type="string"> + <text:p>=AVERAGE(SMALL(F1:F11,{5,2,3}))</text:p> </table:table-cell> - <table:table-cell/> - <table:table-cell table:style-name="ce28"/> - <table:table-cell table:style-name="ce32"/> <table:table-cell office:value-type="string" calcext:value-type="string"> <text:p>AOO 32345</text:p> </table:table-cell> diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx index 2b117b2be56c..0d41e175e335 100644 --- a/sc/source/core/tool/interpr3.cxx +++ b/sc/source/core/tool/interpr3.cxx @@ -31,7 +31,9 @@ #include <scmatrix.hxx> #include <math.h> +#include <cassert> #include <memory> +#include <set> #include <vector> #include <algorithm> #include <comphelper/random.hxx> @@ -3635,29 +3637,73 @@ void ScInterpreter::CalculateSmallLarge(bool bSmall) { if ( !MustHaveParamCount( GetByte(), 2 ) ) return; - double f = ::rtl::math::approxFloor(GetDouble()); - if (f < 1.0) + + std::vector<double> aArray; + GetNumberSequenceArray(1, aArray, false); + auto aArraySize = aArray.size(); + if (aArraySize == 0 || nGlobalError != FormulaError::NONE) { - PushIllegalArgument(); + PushNoValue(); return; } - SCSIZE k = static_cast<SCSIZE>(f); + for (double fArg : aArray) + { + double f = ::rtl::math::approxFloor(fArg); + if (f < 1.0) + { + PushIllegalArgument(); + return; + } + } + + std::vector<SCSIZE> aRankArray; + aRankArray.reserve(aArraySize); + std::transform(aArray.begin(), aArray.end(), std::back_inserter(aRankArray), + [](double f) { return static_cast<SCSIZE>(f); }); + + auto itMaxRank = std::max_element(aRankArray.begin(), aRankArray.end()); + assert(itMaxRank != aRankArray.end()); + SCSIZE k = *itMaxRank; + 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. */ GetNumberSequenceArray(1, aSortArray, false ); SCSIZE nSize = aSortArray.size(); if (nSize == 0 || nGlobalError != FormulaError::NONE || nSize < k) PushNoValue(); - else + else if (aArraySize == 1) { - // 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); } + else + { + std::set<SCSIZE> aIndices; + for (SCSIZE n : aRankArray) + aIndices.insert(bSmall ? n-1 : nSize-n); + // We can spare sorting when the total number of ranks is small enough. + // Find only the elements at given indices if, arbitrarily, the index size is + // smaller than 1/3 of the haystack array's size; just sort it squarely, otherwise. + if (aIndices.size() < nSize/3) + { + auto itBegin = aSortArray.begin(); + for (SCSIZE i : aIndices) + { + auto it = aSortArray.begin() + i; + std::nth_element(itBegin, it, aSortArray.end()); + itBegin = ++it; + } + } + else + std::sort(aSortArray.begin(), aSortArray.end()); + + aArray.clear(); + for (SCSIZE n : aRankArray) + aArray.push_back(aSortArray[bSmall ? n-1 : nSize-n]); + ScMatrixRef pResult = GetNewMat(1, aArraySize, true); + pResult->PutDoubleVector(aArray, 0, 0); + PushMatrix(pResult); + } } void ScInterpreter::ScLarge() |