From 3df80ed78068218e0145e28f307c613081fbcc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 4 Feb 2020 15:09:46 +0100 Subject: [PATCH] [poincare] ParameteredExpression: overrides getVariables to avoid considering parameter as a variable --- apps/solver/equation_store.cpp | 4 +-- poincare/include/poincare/expression.h | 2 +- poincare/include/poincare/expression_node.h | 2 +- poincare/include/poincare/function.h | 2 +- .../include/poincare/parametered_expression.h | 2 ++ poincare/include/poincare/symbol.h | 2 +- poincare/src/expression_node.cpp | 9 +++--- poincare/src/function.cpp | 4 +-- poincare/src/parametered_expression.cpp | 32 +++++++++++++++++++ poincare/src/symbol.cpp | 24 +++++++------- poincare/test/expression_properties.cpp | 10 ++++-- 11 files changed, 66 insertions(+), 27 deletions(-) diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 47838cce0..de792d191 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -146,7 +146,7 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex if (e.type() == ExpressionNode::Type::Unreal) { return Error::EquationUnreal; } - numberOfVariables = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize); + numberOfVariables = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, numberOfVariables); if (numberOfVariables == -1) { return Error::TooManyVariables; } @@ -162,7 +162,7 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex for (int i = 0; i < numberOfDefinedModels(); i++) { const Expression e = modelForRecord(definedRecordAtIndex(i))->standardForm(context, true); assert(!e.isUninitialized() && e.type() != ExpressionNode::Type::Undefined && e.type() != ExpressionNode::Type::Unreal); - int varCount = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return context->expressionTypeForIdentifier(symbol, strlen(symbol)) == Poincare::Context::SymbolAbstractType::Symbol; }, (char *)m_userVariables, Poincare::SymbolAbstract::k_maxNameSize); + int varCount = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return context->expressionTypeForIdentifier(symbol, strlen(symbol)) == Poincare::Context::SymbolAbstractType::Symbol; }, (char *)m_userVariables, Poincare::SymbolAbstract::k_maxNameSize, m_numberOfUserVariables); if (varCount < 0) { m_numberOfUserVariables = Expression::k_maxNumberOfVariables; break; diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index ebe497804..447cf9924 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -183,7 +183,7 @@ public: * If one of the variable lengths overflows maxVariableLength, getVariables * returns -2. */ static constexpr int k_maxNumberOfVariables = 6; - int getVariables(Context * context, ExpressionNode::isVariableTest isVariable, char * variables, int maxVariableLength) const { return node()->getVariables(context, isVariable, variables, maxVariableLength); } + int getVariables(Context * context, ExpressionNode::isVariableTest isVariable, char * variables, int maxVariableLength, int nextVariableIndex = 0) const { return node()->getVariables(context, isVariable, variables, maxVariableLength, nextVariableIndex); } /* 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 diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index d8b18d2a9..fa7cbf5e4 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -173,7 +173,7 @@ public: /*!*/ virtual int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const; /*!*/ virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly); typedef bool (*isVariableTest)(const char * c, Poincare::Context * context); - virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const; + virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const; virtual float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const; bool isOfType(Type * types, int length) const; virtual bool beautifiedExpressionHasUnits() const { return false; } // This must be called on a beautified expression diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h index efebf585a..422140032 100644 --- a/poincare/include/poincare/function.h +++ b/poincare/include/poincare/function.h @@ -26,7 +26,7 @@ public: Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override; int polynomialDegree(Context * context, const char * symbolName) const override; int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const override; - int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; + int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const override; float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const override; private: diff --git a/poincare/include/poincare/parametered_expression.h b/poincare/include/poincare/parametered_expression.h index 991276d65..456a63be0 100644 --- a/poincare/include/poincare/parametered_expression.h +++ b/poincare/include/poincare/parametered_expression.h @@ -14,6 +14,8 @@ public: // Expression bool isParameteredExpression() const override { return true; } Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override; + // Expression properties + int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const override; }; class ParameteredExpression : public Expression { diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index fd64b4e4b..67fe92d5f 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -24,7 +24,7 @@ public: Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override; int polynomialDegree(Context * context, const char * symbolName) const override; int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const override; - int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; + int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const override; float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const override; /* Layout */ diff --git a/poincare/src/expression_node.cpp b/poincare/src/expression_node.cpp index 2fee755c8..4041cd92e 100644 --- a/poincare/src/expression_node.cpp +++ b/poincare/src/expression_node.cpp @@ -40,16 +40,15 @@ Expression ExpressionNode::deepReplaceReplaceableSymbols(Context * context, bool return Expression(this).defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly); } -int ExpressionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { - int numberOfVariables = 0; +int ExpressionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const { for (ExpressionNode * c : children()) { - int n = c->getVariables(context, isVariable, variables, maxSizeVariable); + int n = c->getVariables(context, isVariable, variables, maxSizeVariable, nextVariableIndex); if (n < 0) { return n; } - numberOfVariables = n > numberOfVariables ? n : numberOfVariables; + nextVariableIndex = n; } - return numberOfVariables; + return nextVariableIndex; } float ExpressionNode::characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const { diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index f1d7263f6..edaef13d9 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -35,13 +35,13 @@ int FunctionNode::getPolynomialCoefficients(Context * context, const char * symb return e.getPolynomialCoefficients(context, symbolName, coefficients, symbolicComputation); } -int FunctionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { +int FunctionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const { Function f(this); Expression e = SymbolAbstract::Expand(f, context, true); if (e.isUninitialized()) { return 0; } - return e.getVariables(context, isVariable, variables, maxSizeVariable); + return e.node()->getVariables(context, isVariable, variables, maxSizeVariable, nextVariableIndex); } float FunctionNode::characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const { diff --git a/poincare/src/parametered_expression.cpp b/poincare/src/parametered_expression.cpp index 488b5acb9..98002fd1e 100644 --- a/poincare/src/parametered_expression.cpp +++ b/poincare/src/parametered_expression.cpp @@ -9,6 +9,38 @@ Expression ParameteredExpressionNode::replaceSymbolWithExpression(const SymbolAb return ParameteredExpression(this).replaceSymbolWithExpression(symbol, expression); } +int ParameteredExpressionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const { + int numberOfVariables = childAtIndex(ParameteredExpression::ParameteredChildIndex())->getVariables(context, isVariable, variables, maxSizeVariable, nextVariableIndex); + // Handle exception + if (numberOfVariables < 0) { + return numberOfVariables; + } + /* Remove the parameter symbol from the list of variable if it was added at + * the previous line */ + // Get the parameter symbol + assert(childAtIndex(ParameteredExpression::ParameterChildIndex())->type() == ExpressionNode::Type::Symbol); + SymbolNode * parameterChild = static_cast(childAtIndex(ParameteredExpression::ParameterChildIndex())); + for (int i = nextVariableIndex; i < numberOfVariables; i++) { + if (strcmp(parameterChild->name(), &variables[i]) == 0) { + variables[i] = 0; + numberOfVariables--; + break; + } + } + nextVariableIndex = numberOfVariables; + static_assert(ParameteredExpression::ParameteredChildIndex() == 0 && ParameteredExpression::ParameterChildIndex() == 1, + "ParameteredExpression::getVariables might not be valid"); + for (int i = ParameteredExpression::ParameterChildIndex() + 1; i < numberOfChildren(); i++) { + int n = childAtIndex(i)->getVariables(context, isVariable, variables, maxSizeVariable, nextVariableIndex); + // Handle exception + if (n < 0) { + return n; + } + nextVariableIndex = n; + } + return nextVariableIndex; +} + Expression ParameteredExpression::replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { Expression c = childAtIndex(ParameterChildIndex()); assert(c.type() == ExpressionNode::Type::Symbol); diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 785297220..1bb45ec6f 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -37,31 +37,31 @@ int SymbolNode::getPolynomialCoefficients(Context * context, const char * symbol return Symbol(this).getPolynomialCoefficients(context, symbolName, coefficients, symbolicComputation); } -int SymbolNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const { +int SymbolNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const { // variables is in fact of type char[k_maxNumberOfVariables][maxSizeVariable] - size_t variablesIndex = 0; - while(variables[variablesIndex] != 0) { - variablesIndex+= maxSizeVariable; - } if (isVariable(m_name, context)) { int index = 0; - while (variables[index] != 0) { + while (index < maxSizeVariable*Expression::k_maxNumberOfVariables && variables[index] != 0) { if (strcmp(m_name, &variables[index]) == 0) { - return variablesIndex/maxSizeVariable; + return nextVariableIndex; } index+= maxSizeVariable; } - if ((variablesIndex/maxSizeVariable) < Expression::k_maxNumberOfVariables) { + if (nextVariableIndex < Expression::k_maxNumberOfVariables) { + assert(variables[nextVariableIndex*maxSizeVariable] == 0); if (strlen(m_name) + 1 > (size_t)maxSizeVariable) { return -2; } - strlcpy(&variables[variablesIndex], m_name, maxSizeVariable); - variables[variablesIndex+maxSizeVariable] = 0; - return variablesIndex/maxSizeVariable+1; + strlcpy(&variables[nextVariableIndex*maxSizeVariable], m_name, maxSizeVariable); + nextVariableIndex++; + if (nextVariableIndex < Expression::k_maxNumberOfVariables) { + variables[nextVariableIndex*maxSizeVariable] = 0; + } + return nextVariableIndex; } return -1; } - return variablesIndex/maxSizeVariable; + return nextVariableIndex; } float SymbolNode::characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const { diff --git a/poincare/test/expression_properties.cpp b/poincare/test/expression_properties.cpp index b49397dbe..92ee89c2e 100644 --- a/poincare/test/expression_properties.cpp +++ b/poincare/test/expression_properties.cpp @@ -280,7 +280,7 @@ void assert_expression_has_variables(const char * expression, const char * varia Shared::GlobalContext globalContext; Expression e = parse_expression(expression, &globalContext, false); constexpr static int k_maxVariableSize = Poincare::SymbolAbstract::k_maxNameSize; - char variableBuffer[Expression::k_maxNumberOfVariables+1][k_maxVariableSize] = {{0}}; + char variableBuffer[Expression::k_maxNumberOfVariables][k_maxVariableSize] = {{0}}; int numberOfVariables = e.getVariables(&globalContext, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)variableBuffer, k_maxVariableSize); quiz_assert_print_if_failure(trueNumberOfVariables == numberOfVariables, expression); if (numberOfVariables < 0) { @@ -288,7 +288,7 @@ void assert_expression_has_variables(const char * expression, const char * varia return; } int index = 0; - while (variableBuffer[index][0] != 0 || variables[index][0] != 0) { + while (index < Expression::k_maxNumberOfVariables && (variableBuffer[index][0] != 0 || variables[index][0] != 0)) { quiz_assert_print_if_failure(strcmp(variableBuffer[index], variables[index]) == 0, expression); index++; } @@ -307,11 +307,17 @@ QUIZ_CASE(poincare_properties_get_variables) { assert_expression_has_variables("BBBBBB", variableBuffer5, 1); const char * variableBuffer6[] = {""}; assert_expression_has_variables("a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+aa+bb+cc+dd+ee+ff+gg+hh+ii+jj+kk+ll+mm+nn+oo", variableBuffer6, -1); + assert_expression_has_variables("a+b+c+d+e+f+g", variableBuffer6, -1); // f: x→1+πx+x^2+toto assert_simplify("1+π×x+x^2+toto→f(x)"); const char * variableBuffer7[] = {"tata","toto", ""}; assert_expression_has_variables("f(tata)", variableBuffer7, 2); Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); + + const char * variableBuffer8[] = {"y", ""}; + assert_expression_has_variables("diff(3x,x,0)y-2", variableBuffer8, 1); + const char * variableBuffer9[] = {"a", "b", "c", "d", "e", "f"}; + assert_expression_has_variables("a+b+c+d+e+f", variableBuffer9, 6); } void assert_reduced_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Cartesian, Preferences::AngleUnit angleUnit = Radian, ExpressionNode::SymbolicComputation symbolicComputation = ReplaceAllDefinedSymbolsWithDefinition) {