[poincare] Change Expression::getVariables to get symbols with variable

sizes
This commit is contained in:
Émilie Feral
2018-09-27 10:04:25 +02:00
parent e1287a20bc
commit 5beafd2bf3
12 changed files with 65 additions and 51 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];

View File

@@ -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. */

View File

@@ -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;

View File

@@ -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:

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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) {