diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 24ee2258a..8d1924d48 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -264,14 +264,16 @@ int StoreController::maxNumberOfElements() const { bool StoreController::privateFillColumnWithFormula(Expression formula, ExpressionNode::isVariableTest isVariable) { int currentColumn = selectedColumn(); // Fetch the series used in the formula to compute the size of the filled in series - char variables[Expression::k_maxNumberOfVariables]; - variables[0] = 0; + constexpr static int k_maxSizeOfStoreSymbols = 3; // "V1", "N1", "X1", "Y1" + char variables[Expression::k_maxNumberOfVariables][k_maxSizeOfStoreSymbols]; + variables[0][0] = 0; AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); - formula.getVariables(*(appsContainer->globalContext()), isVariable, variables); + int nbOfVariables = formula.getVariables(*(appsContainer->globalContext()), isVariable, variables, k_maxSizeOfStoreSymbols); + assert(nbOfVariables >= 0); int numberOfValuesToCompute = -1; int index = 0; - while (variables[index] != 0) { - const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); + while (variables[index][0] != 0) { + const char * seriesName = variables[index]; assert(strlen(seriesName) == 2); int series = (int)(seriesName[1] - '0') - 1; assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); diff --git a/apps/solver/equation.h b/apps/solver/equation.h index 3a079e836..851af5ede 100644 --- a/apps/solver/equation.h +++ b/apps/solver/equation.h @@ -14,6 +14,7 @@ public: return false; } Poincare::Expression standardForm(Poincare::Context * context) const; + constexpr static int k_maxVariableSize = 10; private: void tidyStandardForm(); mutable Poincare::Expression m_standardForm; diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 297d0530a..657c56a53 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -80,7 +80,7 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context) { } void EquationStore::approximateSolve(Poincare::Context * context) { - assert(m_variables[0] != 0 && m_variables[1] == 0); + assert(m_variables[0][0] != 0 && m_variables[1][0] == 0); assert(m_type == Type::Monovariable); m_numberOfSolutions = 0; double start = m_intervalApproximateSolutions[0]; @@ -100,17 +100,20 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { tidySolution(); /* 0- Get unknown variables */ - m_variables[0] = 0; + m_variables[0][0] = 0; int numberOfVariables = 0; for (int i = 0; i < numberOfDefinedModels(); i++) { const Expression e = definedModelAtIndex(i)->standardForm(context); if (e.isUninitialized() || e.type() == ExpressionNode::Type::Undefined) { return Error::EquationUndefined; } - numberOfVariables = definedModelAtIndex(i)->standardForm(context).getVariables(*context, Symbol::isVariableSymbol, m_variables); - if (numberOfVariables < 0) { + numberOfVariables = definedModelAtIndex(i)->standardForm(context).getVariables(*context, Symbol::isVariableSymbol, m_variables, Equation::k_maxVariableSize); + if (numberOfVariables == -1) { return Error::TooManyVariables; } + /*if (numberOfVariables == -2) { + return Error::VariableNameTooLong; + }*/ } /* 1- Linear System? */ @@ -150,9 +153,8 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { } else { /* 2- Polynomial & Monovariable? */ assert(numberOfVariables == 1 && numberOfDefinedModels() == 1); - const char x[] = {m_variables[0], 0}; Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; - int degree = definedModelAtIndex(0)->standardForm(context).getPolynomialReducedCoefficients(x, polynomialCoefficients, *context, preferences->angleUnit()); + int degree = definedModelAtIndex(0)->standardForm(context).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, *context, preferences->angleUnit()); if (degree == 2) { /* Polynomial degree <= 2*/ m_type = Type::PolynomialMonovariable; @@ -189,7 +191,9 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression constants[k_maxNumberOfEquations], Context * context) { Preferences::AngleUnit angleUnit = Preferences::sharedPreferences()->angleUnit(); - int n = strlen(m_variables); // n unknown variables + // n unknown variables + int n = 0; + while (m_variables[n++][0] != 0) {} int m = numberOfDefinedModels(); // m equations /* Create the matrix (A | b) for the equation Ax=b */ Matrix Ab; diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 7917b5a02..fcfe2346b 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -84,7 +84,7 @@ private: Equation m_equations[k_maxNumberOfEquations]; Type m_type; - char m_variables[Poincare::Expression::k_maxNumberOfVariables+1]; + char m_variables[Poincare::Expression::k_maxNumberOfVariables+1][Equation::k_maxVariableSize]; int m_numberOfSolutions; Poincare::Layout m_exactSolutionExactLayouts[k_maxNumberOfApproximateSolutions]; Poincare::Layout m_exactSolutionApproximateLayouts[k_maxNumberOfExactSolutions]; diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 581244817..7f4f36afd 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -124,14 +124,16 @@ public: * - (-1) if the expression is not a polynome * - the degree of the polynome otherwise */ int polynomialDegree(Context & context, const char * symbolName) const { return this->node()->polynomialDegree(context, symbolName); } - /* getVariables fills the table variables with the variable present in the - * expression and returns the number of entries in filled in variables. - * For instance getVariables('x+y+2*w/cos(4)') would result in - * variables = « xyw » and would return 3. If the final number of - * variables would overflow the maxNumberOfVariables, getVariables return -1 */ + /* getVariables fills the matrix variables with the symbols present in the + * expression that passes the test isVariable. It returns the number of + * entries filled in variables. For instance, getVariables of + * 'x+y+2*w/cos(4)' would result in variables = {"x", "y", "w"} and would + * return 3. If the final numberof variables would overflow the + * maxNumberOfVariables, getVariables return -1. If one of the variable + * lengths overflow the maxVariableLength; getVariables return -2. */ static constexpr int k_maxNumberOfVariables = 6; - int getVariables(Context & context, ExpressionNode::isVariableTest isVariable, char * variables) const { return node()->getVariables(context, isVariable, variables); } - /* getLinearCoefficients return false if the expression is not linear with + int getVariables(Context & context, ExpressionNode::isVariableTest isVariable, char * variables, int maxVariableLength) const { return node()->getVariables(context, isVariable, variables, maxVariableLength); } +0 /* getLinearCoefficients return false if the expression is not linear with * the variables hold in 'variables'. Otherwise, it fills 'coefficients' with * the coefficients of the variables hold in 'variables' (following the same * order) and 'constant' with the constant of the expression. */ diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index a214a2570..7cb68ddc0 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -104,7 +104,7 @@ public: virtual int polynomialDegree(Context & context, const char * symbolName) const; /*!*/ virtual int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; typedef bool (*isVariableTest)(const char * c); - virtual int getVariables(Context & context, isVariableTest isVariable, char * variables) const; + virtual int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const; virtual float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const; bool isOfType(Type * types, int length) const; diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h index d007a6cfe..716e1ca44 100644 --- a/poincare/include/poincare/function.h +++ b/poincare/include/poincare/function.h @@ -20,7 +20,7 @@ public: Type type() const override { return Type::Function; } int polynomialDegree(Context & context, const char * symbolName) const override; int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; - int getVariables(Context & context, isVariableTest isVariable, char * variables) const override; + int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; private: diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index d0884dd00..09956a374 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -32,7 +32,7 @@ public: Expression replaceSymbolWithExpression(const char * symbol, Expression & expression) override; int polynomialDegree(Context & context, const char * symbolName) const override; int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; - int getVariables(Context & context, isVariableTest isVariable, char * variables) const override; + int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; /* Comparison */ diff --git a/poincare/src/expression_node.cpp b/poincare/src/expression_node.cpp index 439d7b9a1..862d4e227 100644 --- a/poincare/src/expression_node.cpp +++ b/poincare/src/expression_node.cpp @@ -26,12 +26,12 @@ int ExpressionNode::getPolynomialCoefficients(Context & context, const char * sy return Expression(this).defaultGetPolynomialCoefficients(context, symbolName, coefficients); } -int ExpressionNode::getVariables(Context & context, isVariableTest isVariable, char * variables) const { +int ExpressionNode::getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { int numberOfVariables = 0; for (ExpressionNode * c : children()) { - int n = c->getVariables(context, isVariable, variables); + int n = c->getVariables(context, isVariable, variables, maxSizeVariable); if (n < 0) { - return -1; + return n; } numberOfVariables = n > numberOfVariables ? n : numberOfVariables; } diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index 333f4b60d..bc4b1b415 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -17,9 +17,9 @@ int FunctionNode::getPolynomialCoefficients(Context & context, const char * symb return e.getPolynomialCoefficients(context, symbolName, coefficients); } -int FunctionNode::getVariables(Context & context, isVariableTest isVariable, char * variables) const { +int FunctionNode::getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { Expression e = context.expressionForSymbol(Function(this)); - return e.getVariables(context, isVariable, variables); + return e.getVariables(context, isVariable, variables, maxSizeVariable); } float FunctionNode::characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const { diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 633ef36e0..ba1efbeb1 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -45,20 +45,23 @@ int SymbolNode::getPolynomialCoefficients(Context & context, const char * symbol return Symbol(this).getPolynomialCoefficients(context, symbolName, coefficients); } -int SymbolNode::getVariables(Context & context, isVariableTest isVariable, char * variables) const { - size_t variablesLength = strlen(variables); +int SymbolNode::getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { + size_t variablesLength = 0; + while(variables[variablesLength++][0] != 0) {} if (isVariable(m_name)) { - assert(m_name[1] == 0); - char * currentChar = variables; - while (*currentChar != 0) { - if (*currentChar == m_name[0]) { + int index = 0; + while (variables[index][0] != 0) { + if (strcmp(variables[index], m_name) == 0) { return variablesLength; } - currentChar++; + index++; } if (variablesLength < Expression::k_maxNumberOfVariables) { - variables[variablesLength] = m_name[0]; - variables[variablesLength+1] = 0; + if (strlen(m_name) + 1 > maxSizeVariable) { + return -2; + } + strlcpy(variables[variablesLength], m_name, maxSizeVariable); + variables[variablesLength+1][0] = 0; return variablesLength+1; } return -1; diff --git a/poincare/test/properties.cpp b/poincare/test/properties.cpp index 5f699b4fe..9c2d9f2f0 100644 --- a/poincare/test/properties.cpp +++ b/poincare/test/properties.cpp @@ -79,29 +79,31 @@ QUIZ_CASE(poincare_characteristic_range) { assert_parsed_expression_has_characteristic_range("cos(cos(x))", 360.0f); } -void assert_parsed_expression_has_variables(const char * expression, const char * variables) { +void assert_parsed_expression_has_variables(const char * expression, const char variables[][]) { Expression e = parse_expression(expression); quiz_assert(!e.isUninitialized()); - char variableBuffer[Expression::k_maxNumberOfVariables+1] = {0}; - int numberOfVariables = e.getVariables(Poincare::Symbol::isVariableSymbol, variableBuffer); - if (variables == nullptr) { + constexpr static int k_maxVariableSize = 10; + char variableBuffer[Expression::k_maxNumberOfVariables+1][k_maxVariableSize] = {{0}}; + int numberOfVariables = e.getVariables(Poincare::Symbol::isVariableSymbol, variableBuffer, k_maxVariableSize); + if (variables[0][0] == 0) { quiz_assert(numberOfVariables == -1); } else { - quiz_assert(numberOfVariables == strlen(variables)); - char * currentChar = variableBuffer; - while (*variables != 0) { - quiz_assert(*currentChar++ == *variables++); + quiz_assert(numberOfVariables == sizeof(variables)); + int index = 0; + const char * currentChar = variableBuffer[]; + while (variableBuffer[index][0] != 0) { + quiz_assert(strcmp(variableBuffer[index], variables[index]) == 0); } } } QUIZ_CASE(poincare_get_variables) { - assert_parsed_expression_has_variables("x+y", "xy"); - assert_parsed_expression_has_variables("x+y+z+2*t", "xyzt"); - assert_parsed_expression_has_variables("abcdef", "abcdef"); - assert_parsed_expression_has_variables("abcdefg", nullptr); - assert_parsed_expression_has_variables("abcde", "abcde"); - assert_parsed_expression_has_variables("x^2+2*y+k!*A+w", "xykw"); + assert_parsed_expression_has_variables("x+y", {"x","y", ""}); + assert_parsed_expression_has_variables("x+y+z+2*t", {"x","y","z","t",""}); + assert_parsed_expression_has_variables("abcdef", {"a","b","c","d","e","f",""}); + assert_parsed_expression_has_variables("abcdefg", {""}); + assert_parsed_expression_has_variables("abcde", {"a","b","c","d","e",""}); + assert_parsed_expression_has_variables("x^2+2*y+k!*A+w", {"x","y","k","w",""}); } void assert_parsed_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) {