sc/inc/SolverSettings.hxx | 117 ++++++++++++++++- sc/qa/unit/data/ods/tdf158735.ods |binary sc/qa/unit/ucalc_solver.cxx | 29 ++++ sc/source/core/data/SolverSettings.cxx | 220 +++++++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+), 10 deletions(-)
New commits: commit 68b0c3a6ac189eea011d1809480e13f7d33652a6 Author: Rafael Lima <rafael.palma.l...@gmail.com> AuthorDate: Mon Mar 4 19:01:40 2024 +0100 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Sun Mar 24 11:31:55 2024 +0100 tdf#158735 Save solver settings for DEPS and SCO as well When bug tdf#38948 was originally fixed, the solvers DEPS and SCO were not considered. This caused a regression, because setting engine options for these solvers made them never be saved, even in its own sheet. This patch fixes that by incorporating the engine options for DEPS and SCO. Change-Id: I93af712f91da2f7b1ac57ed74f6c2c2d7d411bba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164376 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> (cherry picked from commit 04d884cc99eb66679fb254129b54488bd40e5abf) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164385 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164531 diff --git a/sc/inc/SolverSettings.hxx b/sc/inc/SolverSettings.hxx index ec1ef994a7b8..985e8d30f796 100644 --- a/sc/inc/SolverSettings.hxx +++ b/sc/inc/SolverSettings.hxx @@ -39,11 +39,34 @@ enum SolverParameter SP_LO_ENGINE, // Engine name used in LO SP_MS_ENGINE, // Engine ID used in MSO SP_INTEGER, // Assume all variables are integer (0: no, 1: yes) + // LpSolve, CoinMP and SwarmSolver SP_NON_NEGATIVE, // Assume non negativity (1: yes, 2: no) SP_EPSILON_LEVEL, // Epsilon level SP_LIMIT_BBDEPTH, // Branch and bound depth SP_TIMEOUT, // Time limit to return a solution - SP_ALGORITHM // Algorithm used by the SwarmSolver (1, 2 or 3) + SP_ALGORITHM, // Algorithm used by the SwarmSolver (1, 2 or 3) + // Engine options common for DEPS and SCO + SP_SWARM_SIZE, // Size of Swarm + SP_LEARNING_CYCLES, // Learning Cycles + SP_GUESS_VARIABLE_RANGE, // Variable Bounds Guessing + SP_VARIABLE_RANGE_THRESHOLD, // Variable Bounds Threshold (when guessing) + SP_ACR_COMPARATOR, // Use ACR Comparator (instead of BCH) + SP_RND_STARTING_POINT, // Use Random starting point + SP_STRONGER_PRNG, // Use a stronger random generator (slower) + SP_STAGNATION_LIMIT, // Stagnation Limit + SP_STAGNATION_TOLERANCE, // Stagnation Tolerance + SP_ENHANCED_STATUS, // Show enhanced solver status + // DEPS Options + SP_AGENT_SWITCH_RATE, // Agent Switch Rate (DE Probability) + SP_SCALING_MIN, // DE: Min Scaling Factor (0-1.2) + SP_SCALING_MAX, // DE: Max Scaling Factor (0-1.2) + SP_CROSSOVER_PROB, // DE: Crossover Probability (0-1) + SP_COGNITIVE_CONST, // Cognitive Constant + SP_SOCIAL_CONST, // Social Constant + SP_CONSTRICTION_COEFF, // PS: Constriction Coefficient + SP_MUTATION_PROB, // Mutation Probability (0-0.005) + // SCO Options + SP_LIBRARY_SIZE, // Size of library }; // Starts at 1 to maintain MS compatibility @@ -123,6 +146,28 @@ private: OUString m_sLimitBBDepth; OUString m_sTimeout; OUString m_sAlgorithm; + // DEPS and SCO + OUString m_sSwarmSize; + OUString m_sLearningCycles; + OUString m_sGuessVariableRange; + OUString m_sVariableRangeThreshold; + OUString m_sUseACRComparator; + OUString m_sUseRandomStartingPoint; + OUString m_sUseStrongerPRNG; + OUString m_sStagnationLimit; + OUString m_sTolerance; + OUString m_sEnhancedSolverStatus; + // DEPS only + OUString m_sAgentSwitchRate; + OUString m_sScalingFactorMin; + OUString m_sScalingFactorMax; + OUString m_sCrossoverProbability; + OUString m_sCognitiveConstant; + OUString m_sSocialConstant; + OUString m_sConstrictionCoeff; + OUString m_sMutationProbability; + OUString m_sLibrarySize; + css::uno::Sequence<css::beans::PropertyValue> m_aEngineOptions; std::vector<ModelConstraint> m_aConstraints; @@ -131,7 +176,9 @@ private: // Used to create or read a single solver parameter based on its named range bool ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes = false); + bool ReadDoubleParamValue(SolverParameter eParam, OUString& rValue); void WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted = false); + void WriteDoubleParamValue(SolverParameter eParam, std::u16string_view sValue); // Creates or reads all constraints stored in named ranges void ReadConstraints(); @@ -149,19 +196,46 @@ private: // Maps solver parameters to named ranges std::map<SolverParameter, OUString> m_mNamedRanges - = { { SP_OBJ_CELL, "solver_opt" }, { SP_OBJ_TYPE, "solver_typ" }, - { SP_OBJ_VAL, "solver_val" }, { SP_VAR_CELLS, "solver_adj" }, - { SP_CONSTR_COUNT, "solver_num" }, { SP_LO_ENGINE, "solver_lo_eng" }, - { SP_MS_ENGINE, "solver_eng" }, { SP_INTEGER, "solver_int" }, - { SP_NON_NEGATIVE, "solver_neg" }, { SP_EPSILON_LEVEL, "solver_eps" }, - { SP_LIMIT_BBDEPTH, "solver_bbd" }, { SP_TIMEOUT, "solver_tim" }, - { SP_ALGORITHM, "solver_alg" } }; + = { { SP_OBJ_CELL, "solver_opt" }, + { SP_OBJ_TYPE, "solver_typ" }, + { SP_OBJ_VAL, "solver_val" }, + { SP_VAR_CELLS, "solver_adj" }, + { SP_CONSTR_COUNT, "solver_num" }, + { SP_LO_ENGINE, "solver_lo_eng" }, + { SP_MS_ENGINE, "solver_eng" }, + { SP_INTEGER, "solver_int" }, + { SP_NON_NEGATIVE, "solver_neg" }, + { SP_EPSILON_LEVEL, "solver_eps" }, + { SP_LIMIT_BBDEPTH, "solver_bbd" }, + { SP_TIMEOUT, "solver_tim" }, + { SP_ALGORITHM, "solver_alg" }, + { SP_SWARM_SIZE, "solver_ssz" }, + { SP_LEARNING_CYCLES, "solver_lcy" }, + { SP_GUESS_VARIABLE_RANGE, "solver_gvr" }, + { SP_VARIABLE_RANGE_THRESHOLD, "solver_vrt" }, + { SP_ACR_COMPARATOR, "solver_acr" }, + { SP_RND_STARTING_POINT, "solver_rsp" }, + { SP_STRONGER_PRNG, "solver_prng" }, + { SP_STAGNATION_LIMIT, "solver_slim" }, + { SP_STAGNATION_TOLERANCE, "solver_stol" }, + { SP_ENHANCED_STATUS, "solver_enst" }, + { SP_AGENT_SWITCH_RATE, "solver_asr" }, + { SP_SCALING_MIN, "solver_smin" }, + { SP_SCALING_MAX, "solver_smax" }, + { SP_CROSSOVER_PROB, "solver_crpb" }, + { SP_COGNITIVE_CONST, "solver_cog" }, + { SP_SOCIAL_CONST, "solver_soc" }, + { SP_CONSTRICTION_COEFF, "solver_ccoeff" }, + { SP_MUTATION_PROB, "solver_mtpb" }, + { SP_LIBRARY_SIZE, "solver_lbsz" } }; // Maps LO solver implementation names to MS engine codes std::map<OUString, OUString> SolverNamesToExcelEngines = { { "com.sun.star.comp.Calc.CoinMPSolver", "2" }, // Simplex LP { "com.sun.star.comp.Calc.LpsolveSolver", "2" }, // Simplex LP - { "com.sun.star.comp.Calc.SwarmSolver", "1" } // GRG Nonlinear + { "com.sun.star.comp.Calc.SwarmSolver", "1" }, // GRG Nonlinear + { "com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl", "3" }, // DEPS + { "com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl", "3" } // SCO }; // Maps MS solver engine codes to LO solver implementation names @@ -180,7 +254,30 @@ private: { "EpsilonLevel", { SP_EPSILON_LEVEL, "solver_eps", "int" } }, { "LimitBBDepth", { SP_LIMIT_BBDEPTH, "solver_bbd", "bool" } }, { "Timeout", { SP_TIMEOUT, "solver_tim", "int" } }, - { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } } }; + { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } }, + // SCO and DEPS + { "AssumeNonNegative", { SP_NON_NEGATIVE, "solver_neg", "bool" } }, + { "SwarmSize", { SP_SWARM_SIZE, "solver_ssz", "int" } }, + { "LearningCycles", { SP_LEARNING_CYCLES, "solver_lcy", "int" } }, + { "GuessVariableRange", { SP_GUESS_VARIABLE_RANGE, "solver_gvr", "bool" } }, + { "VariableRangeThreshold", { SP_VARIABLE_RANGE_THRESHOLD, "solver_vrt", "double" } }, + { "UseACRComparator", { SP_ACR_COMPARATOR, "solver_acr", "bool" } }, + { "UseRandomStartingPoint", { SP_RND_STARTING_POINT, "solver_rsp", "bool" } }, + { "UseStrongerPRNG", { SP_STRONGER_PRNG, "solver_prng", "bool" } }, + { "StagnationLimit", { SP_STAGNATION_LIMIT, "solver_slim", "int" } }, + { "Tolerance", { SP_STAGNATION_TOLERANCE, "solver_stol", "double" } }, + { "EnhancedSolverStatus", { SP_ENHANCED_STATUS, "solver_enst", "bool" } }, + // DEPS only + { "AgentSwitchRate", { SP_AGENT_SWITCH_RATE, "solver_asr", "double" } }, + { "DEFactorMin", { SP_SCALING_MIN, "solver_smin", "double" } }, + { "DEFactorMax", { SP_SCALING_MAX, "solver_smax", "double" } }, + { "DECR", { SP_CROSSOVER_PROB, "solver_crpb", "double" } }, + { "PSC1", { SP_COGNITIVE_CONST, "solver_cog", "double" } }, + { "PSC2", { SP_SOCIAL_CONST, "solver_soc", "double" } }, + { "PSWeight", { SP_CONSTRICTION_COEFF, "solver_ccoeff", "double" } }, + { "PSCL", { SP_MUTATION_PROB, "solver_mtpb", "double" } }, + // SCO only + { "LibrarySize", { SP_LIBRARY_SIZE, "solver_lbsz", "int" } } }; // Stores the roots used for named ranges of constraint parts // Items here must be in the same order as in ConstraintPart enum diff --git a/sc/qa/unit/data/ods/tdf158735.ods b/sc/qa/unit/data/ods/tdf158735.ods new file mode 100644 index 000000000000..6003f29bf38a Binary files /dev/null and b/sc/qa/unit/data/ods/tdf158735.ods differ diff --git a/sc/qa/unit/ucalc_solver.cxx b/sc/qa/unit/ucalc_solver.cxx index 7a8d76cc7534..ef16c37fe290 100644 --- a/sc/qa/unit/ucalc_solver.cxx +++ b/sc/qa/unit/ucalc_solver.cxx @@ -130,4 +130,33 @@ CPPUNIT_TEST_FIXTURE(SolverTest, testSingleModel) TestConstraintsModelA(pSettings.get()); } +// Tests if settings for the DEPS and SCO solvers are kept in the file +CPPUNIT_TEST_FIXTURE(SolverTest, tdf158735) +{ + createScDoc("ods/tdf158735.ods"); + ScDocument* pDoc = getScDoc(); + + // Test the non-default values of the DEPS model + ScTable* pTable = pDoc->FetchTable(0); + std::shared_ptr<sc::SolverSettings> pSettings = pTable->GetSolverSettings(); + CPPUNIT_ASSERT(pSettings); + CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl"), + pSettings->GetParameter(SP_LO_ENGINE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.45"), pSettings->GetParameter(SP_AGENT_SWITCH_RATE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.85"), pSettings->GetParameter(SP_CROSSOVER_PROB)); + CPPUNIT_ASSERT_EQUAL(OUString("1500"), pSettings->GetParameter(SP_LEARNING_CYCLES)); + CPPUNIT_ASSERT_EQUAL(OUString("0"), pSettings->GetParameter(SP_ENHANCED_STATUS)); + + // Test the non-default values of the SCO model + pTable = pDoc->FetchTable(1); + pSettings = pTable->GetSolverSettings(); + CPPUNIT_ASSERT(pSettings); + CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl"), + pSettings->GetParameter(SP_LO_ENGINE)); + CPPUNIT_ASSERT_EQUAL(OUString("180"), pSettings->GetParameter(SP_LIBRARY_SIZE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.00055"), pSettings->GetParameter(SP_STAGNATION_TOLERANCE)); + CPPUNIT_ASSERT_EQUAL(OUString("1"), pSettings->GetParameter(SP_RND_STARTING_POINT)); + CPPUNIT_ASSERT_EQUAL(OUString("80"), pSettings->GetParameter(SP_STAGNATION_LIMIT)); +} + CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sc/source/core/data/SolverSettings.cxx b/sc/source/core/data/SolverSettings.cxx index 53fc497c849e..c39c979637bd 100644 --- a/sc/source/core/data/SolverSettings.cxx +++ b/sc/source/core/data/SolverSettings.cxx @@ -11,6 +11,7 @@ #include <global.hxx> #include <table.hxx> #include <docsh.hxx> +#include <rtl/math.hxx> #include <solverutil.hxx> #include <unotools/charclass.hxx> #include <SolverSettings.hxx> @@ -73,6 +74,28 @@ void SolverSettings::Initialize() ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth); ReadParamValue(SP_TIMEOUT, m_sTimeout); ReadParamValue(SP_ALGORITHM, m_sAlgorithm); + // Engine options common for DEPS and SCO + ReadParamValue(SP_SWARM_SIZE, m_sSwarmSize); + ReadParamValue(SP_LEARNING_CYCLES, m_sLearningCycles); + ReadParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange); + ReadDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold); + ReadParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator); + ReadParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint); + ReadParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG); + ReadParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit); + ReadDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance); + ReadParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus); + // DEPS Options + ReadDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate); + ReadDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin); + ReadDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax); + ReadDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability); + ReadDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant); + ReadDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant); + ReadDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff); + ReadDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability); + // SCO Options + ReadParamValue(SP_LIBRARY_SIZE, m_sLibrarySize); } // Returns the current value of the parameter in the object as a string @@ -119,6 +142,63 @@ OUString SolverSettings::GetParameter(SolverParameter eParam) case SP_ALGORITHM: return m_sAlgorithm; break; + case SP_SWARM_SIZE: + return m_sSwarmSize; + break; + case SP_LEARNING_CYCLES: + return m_sLearningCycles; + break; + case SP_GUESS_VARIABLE_RANGE: + return m_sGuessVariableRange; + break; + case SP_VARIABLE_RANGE_THRESHOLD: + return m_sVariableRangeThreshold; + break; + case SP_ACR_COMPARATOR: + return m_sUseACRComparator; + break; + case SP_RND_STARTING_POINT: + return m_sUseRandomStartingPoint; + break; + case SP_STRONGER_PRNG: + return m_sUseStrongerPRNG; + break; + case SP_STAGNATION_LIMIT: + return m_sStagnationLimit; + break; + case SP_STAGNATION_TOLERANCE: + return m_sTolerance; + break; + case SP_ENHANCED_STATUS: + return m_sEnhancedSolverStatus; + break; + case SP_AGENT_SWITCH_RATE: + return m_sAgentSwitchRate; + break; + case SP_SCALING_MIN: + return m_sScalingFactorMin; + break; + case SP_SCALING_MAX: + return m_sScalingFactorMax; + break; + case SP_CROSSOVER_PROB: + return m_sCrossoverProbability; + break; + case SP_COGNITIVE_CONST: + return m_sCognitiveConstant; + break; + case SP_SOCIAL_CONST: + return m_sSocialConstant; + break; + case SP_CONSTRICTION_COEFF: + return m_sConstrictionCoeff; + break; + case SP_MUTATION_PROB: + return m_sMutationProbability; + break; + case SP_LIBRARY_SIZE: + return m_sLibrarySize; + break; default: return ""; } @@ -188,6 +268,75 @@ void SolverSettings::SetParameter(SolverParameter eParam, OUString sValue) m_sAlgorithm = sValue; } break; + case SP_SWARM_SIZE: + m_sSwarmSize = sValue; + break; + case SP_LEARNING_CYCLES: + m_sLearningCycles = sValue; + break; + case SP_GUESS_VARIABLE_RANGE: + m_sGuessVariableRange = sValue; + break; + case SP_VARIABLE_RANGE_THRESHOLD: + m_sVariableRangeThreshold = sValue; + break; + case SP_ACR_COMPARATOR: + { + if (sValue == "0" || sValue == "1") + m_sUseACRComparator = sValue; + } + break; + case SP_RND_STARTING_POINT: + { + if (sValue == "0" || sValue == "1") + m_sUseRandomStartingPoint = sValue; + } + break; + case SP_STRONGER_PRNG: + { + if (sValue == "0" || sValue == "1") + m_sUseStrongerPRNG = sValue; + } + break; + case SP_STAGNATION_LIMIT: + m_sStagnationLimit = sValue; + break; + case SP_STAGNATION_TOLERANCE: + m_sTolerance = sValue; + break; + case SP_ENHANCED_STATUS: + { + if (sValue == "0" || sValue == "1") + m_sEnhancedSolverStatus = sValue; + } + break; + case SP_AGENT_SWITCH_RATE: + m_sAgentSwitchRate = sValue; + break; + case SP_SCALING_MIN: + m_sScalingFactorMin = sValue; + break; + case SP_SCALING_MAX: + m_sScalingFactorMax = sValue; + break; + case SP_CROSSOVER_PROB: + m_sCrossoverProbability = sValue; + break; + case SP_COGNITIVE_CONST: + m_sCognitiveConstant = sValue; + break; + case SP_SOCIAL_CONST: + m_sSocialConstant = sValue; + break; + case SP_CONSTRICTION_COEFF: + m_sConstrictionCoeff = sValue; + break; + case SP_MUTATION_PROB: + m_sMutationProbability = sValue; + break; + case SP_LIBRARY_SIZE: + m_sLibrarySize = sValue; + break; default: break; } @@ -321,12 +470,35 @@ void SolverSettings::SaveSolverSettings() sal_Int32 nConstrCount = m_aConstraints.size(); WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount)); + // Solver engine options WriteParamValue(SP_INTEGER, m_sInteger); WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative); WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel); WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth); WriteParamValue(SP_TIMEOUT, m_sTimeout); WriteParamValue(SP_ALGORITHM, m_sAlgorithm); + // Engine options common for DEPS and SCO + WriteParamValue(SP_SWARM_SIZE, m_sSwarmSize); + WriteParamValue(SP_LEARNING_CYCLES, m_sLearningCycles); + WriteParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange); + WriteDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold); + WriteParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator); + WriteParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint); + WriteParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG); + WriteParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit); + WriteDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance); + WriteParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus); + // DEPS Options + WriteDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate); + WriteDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin); + WriteDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax); + WriteDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability); + WriteDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant); + WriteDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant); + WriteDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff); + WriteDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability); + // SCO Options + WriteParamValue(SP_LIBRARY_SIZE, m_sLibrarySize); if (m_pDocShell) m_pDocShell->SetDocumentModified(); @@ -352,6 +524,26 @@ bool SolverSettings::ReadParamValue(SolverParameter eParam, OUString& rValue, bo return false; } +// Reads a parameter value of type 'double' from the named range and into rValue +bool SolverSettings::ReadDoubleParamValue(SolverParameter eParam, OUString& rValue) +{ + const auto iter = m_mNamedRanges.find(eParam); + assert(iter != m_mNamedRanges.end()); + OUString sRange = iter->second; + ScRangeData* pRangeData + = m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange)); + if (pRangeData) + { + OUString sLocalizedValue = pRangeData->GetSymbol(); + double fValue = rtl::math::stringToDouble(sLocalizedValue, + ScGlobal::getLocaleData().getNumDecimalSep()[0], + ScGlobal::getLocaleData().getNumThousandSep()[0]); + rValue = OUString::number(fValue); + return true; + } + return false; +} + /* Writes a parameter value to the file as a named range. * Argument bQuoted indicates whether the value should be enclosed with quotes or not (used * for string expressions that must be enclosed with quotes) @@ -371,6 +563,22 @@ void SolverSettings::WriteParamValue(SolverParameter eParam, OUString sValue, bo m_pRangeName->insert(pNewEntry); } +// Writes a parameter value of type 'double' to the file as a named range +// The argument 'sValue' uses dot as decimal separator and needs to be localized before +// being written to the file +void SolverSettings::WriteDoubleParamValue(SolverParameter eParam, std::u16string_view sValue) +{ + const auto iter = m_mNamedRanges.find(eParam); + assert(iter != m_mNamedRanges.end()); + OUString sRange = iter->second; + double fValue = rtl::math::stringToDouble(sValue, '.', ','); + OUString sLocalizedValue = rtl::math::doubleToUString( + fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + ScGlobal::getLocaleData().getNumDecimalSep()[0], true); + ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sLocalizedValue); + m_pRangeName->insert(pNewEntry); +} + void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions) { sal_Int32 nOptionsSize = aOptions.getLength(); @@ -394,6 +602,12 @@ void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyVal pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, nValue, css::beans::PropertyState_DIRECT_VALUE); } + if (sParamType == "double") + { + css::uno::Any fValue(sParamValue.toDouble()); + pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, fValue, + css::beans::PropertyState_DIRECT_VALUE); + } if (sParamType == "bool") { // The parameter NonNegative is a special case for MS compatibility @@ -434,6 +648,12 @@ void SolverSettings::SetEngineOptions(css::uno::Sequence<css::beans::PropertyVal aProp.Value >>= nValue; SetParameter(eParamId, OUString::number(nValue)); } + if (sParamType == "double") + { + double fValue = 0; + aProp.Value >>= fValue; + SetParameter(eParamId, OUString::number(fValue)); + } if (sParamType == "bool") { bool bValue = false;