update emoji autocorrect entries from po-files
[LibreOffice.git] / sc / source / core / tool / formulagroup.cxx
blobdfa368909f7ba100745db54ba8391cc925624f54
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <config_features.h>
12 #include "formulagroup.hxx"
13 #include "formulagroupcl.hxx"
14 #include "document.hxx"
15 #include "formulacell.hxx"
16 #include "tokenarray.hxx"
17 #include "compiler.hxx"
18 #include "interpre.hxx"
19 #include "scmatrix.hxx"
20 #include "globalnames.hxx"
22 #include <formula/vectortoken.hxx>
23 #include <officecfg/Office/Common.hxx>
24 #if HAVE_FEATURE_OPENCL
25 #include <opencl/platforminfo.hxx>
26 #endif
27 #include <rtl/bootstrap.hxx>
29 #include <cstdio>
30 #include <unordered_map>
31 #include <vector>
33 #if HAVE_FEATURE_OPENCL
34 #include <opencl/openclwrapper.hxx>
35 #endif
37 namespace sc {
39 FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell** pCells, size_t nRow, size_t nLength ) :
40 mpCells(pCells), mnRow(nRow), mnLength(nLength), mbShared(true) {}
42 FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell* pCell, size_t nRow ) :
43 mpCell(pCell), mnRow(nRow), mnLength(0), mbShared(false) {}
45 size_t FormulaGroupContext::ColKey::Hash::operator ()( const FormulaGroupContext::ColKey& rKey ) const
47 return rKey.mnTab * MAXCOLCOUNT + rKey.mnCol;
50 FormulaGroupContext::ColKey::ColKey( SCTAB nTab, SCCOL nCol ) : mnTab(nTab), mnCol(nCol) {}
52 bool FormulaGroupContext::ColKey::operator== ( const ColKey& r ) const
54 return mnTab == r.mnTab && mnCol == r.mnCol;
57 bool FormulaGroupContext::ColKey::operator!= ( const ColKey& r ) const
59 return !operator==(r);
62 FormulaGroupContext::ColArray::ColArray( NumArrayType* pNumArray, StrArrayType* pStrArray ) :
63 mpNumArray(pNumArray), mpStrArray(pStrArray), mnSize(0)
65 if (mpNumArray)
66 mnSize = mpNumArray->size();
67 else if (mpStrArray)
68 mnSize = mpStrArray->size();
71 FormulaGroupContext::ColArray* FormulaGroupContext::getCachedColArray( SCTAB nTab, SCCOL nCol, size_t nSize )
73 ColArraysType::iterator itColArray = maColArrays.find(ColKey(nTab, nCol));
74 if (itColArray == maColArrays.end())
75 // Not cached for this column.
76 return NULL;
78 ColArray& rCached = itColArray->second;
79 if (nSize > rCached.mnSize)
80 // Cached data array is not long enough for the requested range.
81 return NULL;
83 return &rCached;
86 FormulaGroupContext::ColArray* FormulaGroupContext::setCachedColArray(
87 SCTAB nTab, SCCOL nCol, NumArrayType* pNumArray, StrArrayType* pStrArray )
89 ColArraysType::iterator it = maColArrays.find(ColKey(nTab, nCol));
90 if (it == maColArrays.end())
92 std::pair<ColArraysType::iterator,bool> r =
93 maColArrays.insert(
94 ColArraysType::value_type(ColKey(nTab, nCol), ColArray(pNumArray, pStrArray)));
96 if (!r.second)
97 // Somehow the insertion failed.
98 return NULL;
100 return &r.first->second;
103 // Prior array exists for this column. Overwrite it.
104 ColArray& rArray = it->second;
105 rArray = ColArray(pNumArray, pStrArray);
106 return &rArray;
109 void FormulaGroupContext::ensureStrArray( ColArray& rColArray, size_t nArrayLen )
111 if (rColArray.mpStrArray)
112 return;
114 maStrArrays.push_back(
115 new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL));
116 rColArray.mpStrArray = &maStrArrays.back();
119 void FormulaGroupContext::ensureNumArray( ColArray& rColArray, size_t nArrayLen )
121 if (rColArray.mpNumArray)
122 return;
124 double fNan;
125 rtl::math::setNan(&fNan);
127 maNumArrays.push_back(
128 new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan));
129 rColArray.mpNumArray = &maNumArrays.back();
132 FormulaGroupContext::FormulaGroupContext()
136 FormulaGroupContext::~FormulaGroupContext()
140 namespace {
143 * Input double array consists of segments of NaN's and normal values.
144 * Insert only the normal values into the matrix while skipping the NaN's.
146 void fillMatrix( ScMatrix& rMat, size_t nCol, const double* pNums, size_t nLen )
148 const double* pNum = pNums;
149 const double* pNumEnd = pNum + nLen;
150 const double* pNumHead = NULL;
151 for (; pNum != pNumEnd; ++pNum)
153 if (!rtl::math::isNan(*pNum))
155 if (!pNumHead)
156 // Store the first non-NaN position.
157 pNumHead = pNum;
159 continue;
162 if (pNumHead)
164 // Flush this non-NaN segment to the matrix.
165 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
166 pNumHead = NULL;
170 if (pNumHead)
172 // Flush last non-NaN segment to the matrix.
173 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
177 void flushStrSegment(
178 ScMatrix& rMat, size_t nCol, rtl_uString** pHead, rtl_uString** pCur, rtl_uString** pTop )
180 size_t nOffset = pHead - pTop;
181 std::vector<svl::SharedString> aStrs;
182 aStrs.reserve(pCur - pHead);
183 for (; pHead != pCur; ++pHead)
184 aStrs.push_back(svl::SharedString(*pHead, *pHead));
186 rMat.PutString(&aStrs[0], aStrs.size(), nCol, nOffset);
189 void fillMatrix( ScMatrix& rMat, size_t nCol, rtl_uString** pStrs, size_t nLen )
191 rtl_uString** p = pStrs;
192 rtl_uString** pEnd = p + nLen;
193 rtl_uString** pHead = NULL;
194 for (; p != pEnd; ++p)
196 if (*p)
198 if (!pHead)
199 // Store the first non-empty string position.
200 pHead = p;
202 continue;
205 if (pHead)
207 // Flush this non-empty segment to the matrix.
208 flushStrSegment(rMat, nCol, pHead, p, pStrs);
209 pHead = NULL;
213 if (pHead)
215 // Flush last non-empty segment to the matrix.
216 flushStrSegment(rMat, nCol, pHead, p, pStrs);
220 void fillMatrix( ScMatrix& rMat, size_t nCol, const double* pNums, rtl_uString** pStrs, size_t nLen )
222 if (!pStrs)
224 fillMatrix(rMat, nCol, pNums, nLen);
225 return;
228 const double* pNum = pNums;
229 const double* pNumHead = NULL;
230 rtl_uString** pStr = pStrs;
231 rtl_uString** pStrEnd = pStr + nLen;
232 rtl_uString** pStrHead = NULL;
234 for (; pStr != pStrEnd; ++pStr, ++pNum)
236 if (*pStr)
238 // String cell exists.
240 if (pNumHead)
242 // Flush this numeric segment to the matrix.
243 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
244 pNumHead = NULL;
247 if (!pStrHead)
248 // Store the first non-empty string position.
249 pStrHead = pStr;
251 continue;
254 // No string cell. Check the numeric cell value.
256 if (pStrHead)
258 // Flush this non-empty string segment to the matrix.
259 flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
260 pStrHead = NULL;
263 if (!rtl::math::isNan(*pNum))
265 // Numeric cell exists.
266 if (!pNumHead)
267 // Store the first non-NaN position.
268 pNumHead = pNum;
270 continue;
273 // Empty cell. No action required.
276 if (pStrHead)
278 // Flush the last non-empty segment to the matrix.
279 flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
281 else if (pNumHead)
283 // Flush the last numeric segment to the matrix.
284 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
290 CompiledFormula::CompiledFormula() {}
292 CompiledFormula::~CompiledFormula() {}
294 FormulaGroupInterpreterSoftware::FormulaGroupInterpreterSoftware() : FormulaGroupInterpreter()
298 ScMatrixRef FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix& /*rMat*/)
300 return ScMatrixRef();
303 CompiledFormula* FormulaGroupInterpreterSoftware::createCompiledFormula(
304 ScFormulaCellGroup& /*rGroup*/, ScTokenArray& /*rCode*/ )
306 return NULL;
309 bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddress& rTopPos,
310 ScFormulaCellGroupRef& xGroup,
311 ScTokenArray& rCode)
313 typedef std::unordered_map<const formula::FormulaToken*, formula::FormulaTokenRef> CachedTokensType;
315 // Decompose the group into individual cells and calculate them individually.
317 // The caller must ensure that the top position is the start position of
318 // the group.
320 ScAddress aTmpPos = rTopPos;
321 std::vector<formula::FormulaTokenRef> aResults;
322 aResults.reserve(xGroup->mnLength);
323 CachedTokensType aCachedTokens;
325 double fNan;
326 rtl::math::setNan(&fNan);
328 for (SCROW i = 0; i < xGroup->mnLength; ++i, aTmpPos.IncRow())
330 ScTokenArray aCode2;
331 for (const formula::FormulaToken* p = rCode.First(); p; p = rCode.Next())
333 CachedTokensType::iterator it = aCachedTokens.find(p);
334 if (it != aCachedTokens.end())
336 // This token is cached. Use the cached one.
337 aCode2.AddToken(*it->second);
338 continue;
341 switch (p->GetType())
343 case formula::svSingleVectorRef:
345 const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
346 const formula::VectorRefArray& rArray = p2->GetArray();
348 rtl_uString* pStr = NULL;
349 double fVal = fNan;
350 if (static_cast<size_t>(i) < p2->GetArrayLength())
352 if (rArray.mpStringArray)
353 // See if the cell is of string type.
354 pStr = rArray.mpStringArray[i];
356 if (!pStr && rArray.mpNumericArray)
357 fVal = rArray.mpNumericArray[i];
360 if (pStr)
362 // This is a string cell.
363 svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
364 aCode2.AddString(rPool.intern(OUString(pStr)));
366 else if (rtl::math::isNan(fVal))
367 // Value of NaN represents an empty cell.
368 aCode2.AddToken(ScEmptyCellToken(false, false));
369 else
370 // Numeric cell.
371 aCode2.AddDouble(fVal);
373 break;
374 case formula::svDoubleVectorRef:
376 const formula::DoubleVectorRefToken* p2 = static_cast<const formula::DoubleVectorRefToken*>(p);
377 const std::vector<formula::VectorRefArray>& rArrays = p2->GetArrays();
378 size_t nColSize = rArrays.size();
379 size_t nRowStart = p2->IsStartFixed() ? 0 : i;
380 size_t nRowEnd = p2->GetRefRowSize() - 1;
381 if (!p2->IsEndFixed())
382 nRowEnd += i;
383 size_t nRowSize = nRowEnd - nRowStart + 1;
384 ScMatrixRef pMat(new ScMatrix(nColSize, nRowSize));
386 size_t nDataRowEnd = p2->GetArrayLength() - 1;
387 if (nRowStart > nDataRowEnd)
388 // Referenced rows are all empty.
389 nRowSize = 0;
390 else if (nRowEnd > nDataRowEnd)
391 // Data array is shorter than the row size of the reference. Truncate it to the data.
392 nRowSize -= nRowEnd - nDataRowEnd;
394 for (size_t nCol = 0; nCol < nColSize; ++nCol)
396 const formula::VectorRefArray& rArray = rArrays[nCol];
397 if (rArray.mpStringArray)
399 if (rArray.mpNumericArray)
401 // Mixture of string and numeric values.
402 const double* pNums = rArray.mpNumericArray;
403 pNums += nRowStart;
404 rtl_uString** pStrs = rArray.mpStringArray;
405 pStrs += nRowStart;
406 fillMatrix(*pMat, nCol, pNums, pStrs, nRowSize);
408 else
410 // String cells only.
411 rtl_uString** pStrs = rArray.mpStringArray;
412 pStrs += nRowStart;
413 fillMatrix(*pMat, nCol, pStrs, nRowSize);
416 else if (rArray.mpNumericArray)
418 // Numeric cells only.
419 const double* pNums = rArray.mpNumericArray;
420 pNums += nRowStart;
421 fillMatrix(*pMat, nCol, pNums, nRowSize);
425 if (p2->IsStartFixed() && p2->IsEndFixed())
427 // Cached the converted token for absolute range referene.
428 ScComplexRefData aRef;
429 ScRange aRefRange = rTopPos;
430 aRefRange.aEnd.SetRow(rTopPos.Row() + nRowEnd);
431 aRef.InitRange(aRefRange);
432 formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
433 aCachedTokens.insert(CachedTokensType::value_type(p, xTok));
434 aCode2.AddToken(*xTok);
436 else
438 ScMatrixToken aTok(pMat);
439 aCode2.AddToken(aTok);
442 break;
443 default:
444 aCode2.AddToken(*p);
448 ScFormulaCell* pDest = rDoc.GetFormulaCell(aTmpPos);
449 if (!pDest)
450 return false;
452 ScCompiler aComp(&rDoc, aTmpPos, aCode2);
453 aComp.CompileTokenArray();
454 ScInterpreter aInterpreter(pDest, &rDoc, aTmpPos, aCode2);
455 aInterpreter.Interpret();
456 aResults.push_back(aInterpreter.GetResultToken());
457 } // for loop end (xGroup->mnLength)
459 if (!aResults.empty())
460 rDoc.SetFormulaResults(rTopPos, &aResults[0], aResults.size());
462 return true;
465 FormulaGroupInterpreter *FormulaGroupInterpreter::msInstance = NULL;
467 void FormulaGroupInterpreter::MergeCalcConfig(const ScDocument& rDoc)
469 maCalcConfig = ScInterpreter::GetGlobalConfig();
470 maCalcConfig.MergeDocumentSpecific(rDoc.GetCalcConfig());
473 /// load and/or configure the correct formula group interpreter
474 FormulaGroupInterpreter *FormulaGroupInterpreter::getStatic()
476 if ( !msInstance )
478 #if HAVE_FEATURE_OPENCL
479 const ScCalcConfig& rConfig = ScInterpreter::GetGlobalConfig();
480 if (officecfg::Office::Common::Misc::UseOpenCL::get())
481 switchOpenCLDevice(rConfig.maOpenCLDevice, rConfig.mbOpenCLAutoSelect, false);
482 #endif
483 static bool bAllowSoftwareInterpreter = (getenv("SC_ALLOW_BROKEN_SOFTWARE_INTERPRETER") != NULL);
485 if ( !msInstance && bAllowSoftwareInterpreter ) // software fallback
487 SAL_INFO("sc.formulagroup", "Create S/W interpreter");
488 msInstance = new sc::FormulaGroupInterpreterSoftware();
492 return msInstance;
495 #if HAVE_FEATURE_OPENCL
496 void FormulaGroupInterpreter::fillOpenCLInfo(std::vector<OpenCLPlatformInfo>& rPlatforms)
498 const std::vector<OpenCLPlatformInfo>& rPlatformsFromWrapper =
499 ::opencl::fillOpenCLInfo();
501 rPlatforms.assign(rPlatformsFromWrapper.begin(), rPlatformsFromWrapper.end());
504 bool FormulaGroupInterpreter::switchOpenCLDevice(const OUString& rDeviceId, bool bAutoSelect, bool bForceEvaluation)
506 bool bOpenCLEnabled = officecfg::Office::Common::Misc::UseOpenCL::get();
507 static bool bAllowSoftwareInterpreter = (getenv("SC_ALLOW_BROKEN_SOFTWARE_INTERPRETER") != NULL);
508 if (!bOpenCLEnabled || (bAllowSoftwareInterpreter && rDeviceId == OPENCL_SOFTWARE_DEVICE_CONFIG_NAME))
510 if(msInstance)
512 // if we already have a software interpreter don't delete it
513 if(dynamic_cast<sc::FormulaGroupInterpreterSoftware*>(msInstance))
514 return true;
516 delete msInstance;
519 msInstance = new sc::FormulaGroupInterpreterSoftware();
520 return true;
522 bool bSuccess = ::opencl::switchOpenCLDevice(&rDeviceId, bAutoSelect, bForceEvaluation);
523 if(!bSuccess)
524 return false;
526 delete msInstance;
527 msInstance = NULL;
529 if ( officecfg::Office::Common::Misc::UseOpenCL::get() )
531 msInstance = new sc::opencl::FormulaGroupInterpreterOpenCL();
532 return msInstance != NULL;
535 return false;
538 void FormulaGroupInterpreter::getOpenCLDeviceInfo(sal_Int32& rDeviceId, sal_Int32& rPlatformId)
540 rDeviceId = -1;
541 rPlatformId = -1;
542 bool bOpenCLEnabled = officecfg::Office::Common::Misc::UseOpenCL::get();
543 if(!bOpenCLEnabled)
544 return;
546 size_t aDeviceId = static_cast<size_t>(-1);
547 size_t aPlatformId = static_cast<size_t>(-1);
549 ::opencl::getOpenCLDeviceInfo(aDeviceId, aPlatformId);
550 rDeviceId = aDeviceId;
551 rPlatformId = aPlatformId;
554 void FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly()
556 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
557 officecfg::Office::Common::Misc::UseOpenCL::set(true, batch);
558 batch->commit();
560 ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
562 aConfig.mbOpenCLSubsetOnly = false;
563 aConfig.mnOpenCLMinimumFormulaGroupSize = 2;
565 ScInterpreter::SetGlobalConfig(aConfig);
568 #endif
570 } // namespace sc
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */