Kaydet (Commit) e22ab5e6 authored tarafından Takeshi Abe's avatar Takeshi Abe Kaydeden (comit) Eike Rathke

Resolves: i#32345 Make LARGE()/SMALL() return an array

... if the second parameter is an array.

This change follows their specification in ODF 1.2.

Change-Id: I45c8923f462e9477e1234b47e39dcdd8d2198784
Reviewed-on: https://gerrit.libreoffice.org/62541
Tested-by: Jenkins
Reviewed-by: 's avatarEike Rathke <erack@redhat.com>
üst 973a3dd9
...@@ -4752,13 +4752,15 @@ ...@@ -4752,13 +4752,15 @@
<table:table-cell table:number-columns-repeated="2"/> <table:table-cell table:number-columns-repeated="2"/>
</table:table-row> </table:table-row>
<table:table-row table:style-name="ro8"> <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"> <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>10</text:p> <text:p>9</text:p>
</table:table-cell> </table:table-cell>
<table:table-cell office:value-type="float" office:value="9" calcext:value-type="float"> <table:table-cell office:value-type="float" office:value="9" calcext:value-type="float">
<text:p>9</text:p> <text:p>9</text:p>
</table:table-cell> </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"> <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> <text:p>=AVERAGE(LARGE(I29:I38,{1,2,3}))</text:p>
</table:table-cell> </table:table-cell>
......
...@@ -4155,12 +4155,18 @@ ...@@ -4155,12 +4155,18 @@
<table:table-cell table:number-columns-repeated="2"/> <table:table-cell table:number-columns-repeated="2"/>
</table:table-row> </table:table-row>
<table:table-row table:style-name="ro6"> <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"> <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>4</text:p> <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:table-cell table:style-name="ce28"/>
<table:table-cell table:style-name="ce32"/>
<table:table-cell office:value-type="string" calcext:value-type="string"> <table:table-cell office:value-type="string" calcext:value-type="string">
<text:p>AOO 32345</text:p> <text:p>AOO 32345</text:p>
</table:table-cell> </table:table-cell>
......
...@@ -31,7 +31,9 @@ ...@@ -31,7 +31,9 @@
#include <scmatrix.hxx> #include <scmatrix.hxx>
#include <math.h> #include <math.h>
#include <cassert>
#include <memory> #include <memory>
#include <set>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <comphelper/random.hxx> #include <comphelper/random.hxx>
...@@ -3635,29 +3637,73 @@ void ScInterpreter::CalculateSmallLarge(bool bSmall) ...@@ -3635,29 +3637,73 @@ void ScInterpreter::CalculateSmallLarge(bool bSmall)
{ {
if ( !MustHaveParamCount( GetByte(), 2 ) ) if ( !MustHaveParamCount( GetByte(), 2 ) )
return; 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; 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; 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 ); GetNumberSequenceArray(1, aSortArray, false );
SCSIZE nSize = aSortArray.size(); SCSIZE nSize = aSortArray.size();
if (nSize == 0 || nGlobalError != FormulaError::NONE || nSize < k) if (nSize == 0 || nGlobalError != FormulaError::NONE || nSize < k)
PushNoValue(); 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); vector<double>::iterator iPos = aSortArray.begin() + (bSmall ? k-1 : nSize-k);
::std::nth_element( aSortArray.begin(), iPos, aSortArray.end()); ::std::nth_element( aSortArray.begin(), iPos, aSortArray.end());
PushDouble( *iPos); 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() void ScInterpreter::ScLarge()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment