sc: extend mismatched-extent matrix arithmetic to the longer side
Element-wise matrix arithmetic between non-broadcast operands
of unequal length used to clip the result to the shorter side
and lose the trailing slots. The result should follow the
longer length, with #N/A in the slots past the shorter
operand.
Pair non-broadcast operands to the larger extent in
lcl_GetExtent (renamed from lcl_GetMinExtent), and switch the
per-cell out-of-bounds error in ExecuteBinaryOp from #VALUE!
to #N/A so the visible result matches the spec.
testMatrixArithmeticMismatchedExtent multiplies A1:A4 by the
column vector {1|2|3} and checks that the fourth row holds
Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>
Change-Id: I34eb5b14c77f8d414677c8ebeaf7e331c413c2a3
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5651
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit b4957ed105bcaa4a73666a6a5e119d523284b0c9)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207872
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
46394856a9
commit
47c226418e
@@ -2182,6 +2182,31 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testExternalRefUnresolved)
|
||||
#endif
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testMatrixArithmeticMismatchedExtent)
|
||||
{
|
||||
// Range arithmetic between operands of unequal non-broadcast
|
||||
// lengths extends the result to the longer length and fills the
|
||||
// slots past the shorter operand with #N/A.
|
||||
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
|
||||
m_pDoc->InsertTab(0, u"Test"_ustr);
|
||||
|
||||
m_pDoc->SetValue(ScAddress(0, 0, 0), 2.0);
|
||||
m_pDoc->SetValue(ScAddress(0, 1, 0), 4.0);
|
||||
m_pDoc->SetValue(ScAddress(0, 2, 0), 7.0);
|
||||
m_pDoc->SetValue(ScAddress(0, 3, 0), 7.0);
|
||||
|
||||
ScMarkData aMark(m_pDoc->GetSheetLimits());
|
||||
aMark.SelectOneTable(0);
|
||||
m_pDoc->InsertMatrixFormula(1, 0, 1, 3, aMark, u"=A1:A4 * {1|2|3}"_ustr);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1, 0, 0)));
|
||||
CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(1, 1, 0)));
|
||||
CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(1, 2, 0)));
|
||||
CPPUNIT_ASSERT_EQUAL(FormulaError::NotAvailable, m_pDoc->GetErrCode(ScAddress(1, 3, 0)));
|
||||
|
||||
m_pDoc->DeleteTab(0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testMatrixOp)
|
||||
{
|
||||
m_pDoc->InsertTab(0, u"Test"_ustr);
|
||||
|
||||
@@ -1218,17 +1218,16 @@ void ScInterpreter::ScMatTrans()
|
||||
PushIllegalParameter();
|
||||
}
|
||||
|
||||
/** Minimum extent of one result matrix dimension.
|
||||
For a row or column vector to be replicated the larger matrix dimension is
|
||||
returned, else the smaller dimension.
|
||||
*/
|
||||
static SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 )
|
||||
// Pair non-broadcast operands to the larger extent so the longer
|
||||
// side keeps its data. A single-cell side broadcasts to the other
|
||||
// side's length.
|
||||
static SCSIZE lcl_GetExtent( SCSIZE n1, SCSIZE n2 )
|
||||
{
|
||||
if (n1 == 1)
|
||||
return n2;
|
||||
else if (n2 == 1)
|
||||
return n1;
|
||||
else if (n1 < n2)
|
||||
else if (n1 > n2)
|
||||
return n1;
|
||||
else
|
||||
return n2;
|
||||
@@ -1237,30 +1236,30 @@ static SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 )
|
||||
static ScMatrixRef lcl_MatrixCalculation(
|
||||
const ScMatrix& rMat1, const ScMatrix& rMat2, ScInterpreter* pInterpreter, const ScMatrix::CalculateOpFunction& Op)
|
||||
{
|
||||
SCSIZE nC1, nC2, nMinC;
|
||||
SCSIZE nR1, nR2, nMinR;
|
||||
SCSIZE nC1, nC2, nExtentC;
|
||||
SCSIZE nR1, nR2, nExtentR;
|
||||
rMat1.GetDimensions(nC1, nR1);
|
||||
rMat2.GetDimensions(nC2, nR2);
|
||||
nMinC = lcl_GetMinExtent( nC1, nC2);
|
||||
nMinR = lcl_GetMinExtent( nR1, nR2);
|
||||
ScMatrixRef xResMat = pInterpreter->GetNewMat(nMinC, nMinR, /*bEmpty*/true);
|
||||
nExtentC = lcl_GetExtent( nC1, nC2);
|
||||
nExtentR = lcl_GetExtent( nR1, nR2);
|
||||
ScMatrixRef xResMat = pInterpreter->GetNewMat(nExtentC, nExtentR, /*bEmpty*/true);
|
||||
if (xResMat)
|
||||
xResMat->ExecuteBinaryOp(nMinC, nMinR, rMat1, rMat2, pInterpreter, Op);
|
||||
xResMat->ExecuteBinaryOp(nExtentC, nExtentR, rMat1, rMat2, pInterpreter, Op);
|
||||
return xResMat;
|
||||
}
|
||||
|
||||
ScMatrixRef ScInterpreter::MatConcat(const ScMatrixRef& pMat1, const ScMatrixRef& pMat2)
|
||||
{
|
||||
SCSIZE nC1, nC2, nMinC;
|
||||
SCSIZE nR1, nR2, nMinR;
|
||||
SCSIZE nC1, nC2, nExtentC;
|
||||
SCSIZE nR1, nR2, nExtentR;
|
||||
pMat1->GetDimensions(nC1, nR1);
|
||||
pMat2->GetDimensions(nC2, nR2);
|
||||
nMinC = lcl_GetMinExtent( nC1, nC2);
|
||||
nMinR = lcl_GetMinExtent( nR1, nR2);
|
||||
ScMatrixRef xResMat = GetNewMat(nMinC, nMinR, /*bEmpty*/true);
|
||||
nExtentC = lcl_GetExtent( nC1, nC2);
|
||||
nExtentR = lcl_GetExtent( nR1, nR2);
|
||||
ScMatrixRef xResMat = GetNewMat(nExtentC, nExtentR, /*bEmpty*/true);
|
||||
if (xResMat)
|
||||
{
|
||||
xResMat->MatConcat(nMinC, nMinR, pMat1, pMat2, mrContext, mrDoc.GetSharedStringPool());
|
||||
xResMat->MatConcat(nExtentC, nExtentR, pMat1, pMat2, mrContext, mrDoc.GetSharedStringPool());
|
||||
}
|
||||
return xResMat;
|
||||
}
|
||||
|
||||
@@ -3163,7 +3163,10 @@ void ScMatrixImpl::ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatri
|
||||
}
|
||||
}
|
||||
else
|
||||
aOutPos = maMat.set(aOutPos, CreateDoubleError(FormulaError::NoValue));
|
||||
// Out of bounds against the shorter operand.
|
||||
// Mismatched-extent arithmetic fills these slots
|
||||
// with #N/A so the longer side still has a result.
|
||||
aOutPos = maMat.set(aOutPos, CreateDoubleError(FormulaError::NotAvailable));
|
||||
aOutPos = MatrixImplType::next_position(aOutPos);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user