[poincare] Fix replaceReplaceableSymbols for parametered expressions

Scenario: ans*int(0,x,0,0)->x then cos(x) crashed because replaceReplaceableSymbols
would not care if it was replacing parameters in a parametered
expression
This commit is contained in:
Léa Saviot
2020-03-31 11:00:43 +02:00
committed by EmilieNumworks
parent 764cf1087a
commit 94fb5815f9
10 changed files with 81 additions and 23 deletions

View File

@@ -76,6 +76,7 @@ class Expression : public TreeHandle {
friend class NthRoot;
friend class Number;
friend class Opposite;
friend class ParameteredExpression;
friend class Parenthesis;
friend class PermuteCoefficient;
friend class Power;
@@ -347,8 +348,8 @@ protected:
Expression defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression);
/* 'deepReplaceReplaceableSymbols' returns an uninitialized expression if it
* is circularly defined. Same convention as for 'ExpressionWithoutSymbols'.*/
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) { return node()->deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly); }
Expression defaultReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly);
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { return node()->deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); }
Expression defaultReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
/* Simplification */
void beautifyAndApproximateScalar(Expression * simplifiedExpression, Expression * approximateExpression, ExpressionNode::ReductionContext userReductionContext, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);

View File

@@ -172,7 +172,7 @@ public:
/*!*/ virtual Expression setSign(Sign s, ReductionContext reductionContext);
virtual int polynomialDegree(Context * context, const char * symbolName) const;
/*!*/ virtual int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const;
/*!*/ virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly);
/*!*/ virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
typedef bool (*isVariableTest)(const char * c, Poincare::Context * context);
virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const;
virtual float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const;

View File

@@ -41,7 +41,7 @@ private:
int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
// Simplification
Expression shallowReduce(ReductionContext reductionContext) override;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) override;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) override;
LayoutShape leftLayoutShape() const override { return strlen(m_name) > 1 ? LayoutShape::MoreLetters : LayoutShape::OneLetter; };
LayoutShape rightLayoutShape() const override { return LayoutShape::BoundaryPunctuation; }
@@ -60,7 +60,7 @@ public:
// Simplification
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly);
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
};
}

View File

@@ -14,6 +14,8 @@ public:
// Expression
bool isParameteredExpression() const override { return true; }
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) override;
// Expression properties
int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const override;
};
@@ -36,6 +38,8 @@ public:
* f(X)=diff(cos(x),x,X), X being an unknown. ReplaceUnknownInExpression does
* that. */
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
Symbol parameter();
protected:
ParameteredExpression(const ParameteredExpressionNode * node) : Expression(node) {}
};

View File

@@ -34,7 +34,7 @@ public:
/* Simplification */
Expression shallowReduce(ReductionContext reductionContext) override;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) override;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) override;
LayoutShape leftLayoutShape() const override;
/* Approximation */
@@ -62,6 +62,7 @@ public:
// Symbol properties
bool isSystemSymbol() const { return node()->isUnknown(); }
bool hasSameNameAs(Symbol & other) const;
const char * name() const { return node()->name(); }
// IsVariable tests
static bool isSeriesSymbol(const char * c, Poincare::Context * context);
@@ -71,7 +72,7 @@ public:
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const;
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly);
Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
private:
SymbolNode * node() const { return static_cast<SymbolNode *>(Expression::node()); }
};

View File

@@ -394,10 +394,10 @@ void Expression::defaultSetChildrenInPlace(Expression other) {
}
}
Expression Expression::defaultReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
Expression Expression::defaultReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
int nbChildren = numberOfChildren();
for (int i = 0; i < nbChildren; i++) {
Expression c = childAtIndex(i).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly);
Expression c = childAtIndex(i).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount);
if (c.isUninitialized()) { // the expression is circularly defined, escape
return Expression();
}
@@ -746,7 +746,7 @@ Expression Expression::ExpressionWithoutSymbols(Expression e, Context * context,
break;
}
didReplace = false;
e = e.deepReplaceReplaceableSymbols(context, &didReplace, replaceFunctionsOnly);
e = e.deepReplaceReplaceableSymbols(context, &didReplace, replaceFunctionsOnly, 0);
if (e.isUninitialized()) { // the expression is circularly defined, escape
replacementCount = k_maxSymbolReplacementsCount;
}

View File

@@ -36,8 +36,8 @@ int ExpressionNode::getPolynomialCoefficients(Context * context, const char * sy
return Expression(this).defaultGetPolynomialCoefficients(context, symbolName, coefficients);
}
Expression ExpressionNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
return Expression(this).defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly);
Expression ExpressionNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
return Expression(this).defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount);
}
int ExpressionNode::getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const {

View File

@@ -65,8 +65,8 @@ Expression FunctionNode::shallowReduce(ReductionContext reductionContext) {
return Function(this).shallowReduce(reductionContext); // This uses Symbol::shallowReduce
}
Expression FunctionNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
return Function(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly);
Expression FunctionNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
return Function(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount);
}
Evaluation<float> FunctionNode::approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
@@ -133,13 +133,15 @@ Expression Function::shallowReduce(ExpressionNode::ReductionContext reductionCon
return result.deepReduce(reductionContext);
}
Expression Function::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
// Replace replaceable symbols in child
Expression self = defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly);
if (self.isUninitialized()) { // if the child is circularly defined, escape
return self;
Expression Function::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
{
// Replace replaceable symbols in child
Expression self = defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly ,parameteredAncestorsCount);
if (self.isUninitialized()) { // if the child is circularly defined, escape
return self;
}
assert(*this == self);
}
assert(*this == self);
Expression e = context->expressionForSymbolAbstract(*this, false);
if (e.isUninitialized()) {
return *this;

View File

@@ -9,6 +9,10 @@ Expression ParameteredExpressionNode::replaceSymbolWithExpression(const SymbolAb
return ParameteredExpression(this).replaceSymbolWithExpression(symbol, expression);
}
Expression ParameteredExpressionNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
return ParameteredExpression(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount);
}
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
@@ -62,4 +66,31 @@ Expression ParameteredExpression::replaceSymbolWithExpression(const SymbolAbstra
return *this;
}
Expression ParameteredExpression::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
/* All children replaceable symbols should be replaced apart from symbols that
* are parameters in parametered expressions.*/
int childrenCount = numberOfChildren();
for (int i = 0; i < childrenCount; i++) {
if (i == ParameterChildIndex()) {
// Do not replace symbols in the parameter child
continue;
}
/* In the parametered child, increase the parametered ancestors count so
* that when replacing symbols, the expressions check that the symbols are
* not the parametered symbols. */
bool shouldIncreaseParameteredAncestorsCount = i == ParameteredChildIndex();
Expression c = childAtIndex(i).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount + (shouldIncreaseParameteredAncestorsCount ? 1 : 0));
if (c.isUninitialized()) { // the expression is circularly defined, escape
return Expression();
}
}
return *this;
}
Symbol ParameteredExpression::parameter() {
Expression e = childAtIndex(ParameteredExpression::ParameterChildIndex());
assert(e.type() == ExpressionNode::Type::Symbol);
return static_cast<Symbol&>(e);
}
}

View File

@@ -94,8 +94,8 @@ Expression SymbolNode::shallowReduce(ReductionContext reductionContext) {
return Symbol(this).shallowReduce(reductionContext);
}
Expression SymbolNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
return Symbol(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly);
Expression SymbolNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
return Symbol(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount);
}
ExpressionNode::LayoutShape SymbolNode::leftLayoutShape() const {
@@ -134,6 +134,10 @@ Symbol Symbol::Builder(CodePoint name) {
return Symbol::Builder(buffer, codePointLength);
}
bool Symbol::hasSameNameAs(Symbol & other) const {
return (strcmp(other.name(), name()) == 0);
}
bool Symbol::isSeriesSymbol(const char * c, Poincare::Context * context) {
// [NV][1-3]
if (c[2] == 0 && (c[0] == 'N' || c[0] == 'V') && c[1] >= '1' && c[1] <= '3') {
@@ -218,10 +222,25 @@ int Symbol::getPolynomialCoefficients(Context * context, const char * symbolName
return 0;
}
Expression Symbol::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly) {
Expression Symbol::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) {
if (replaceFunctionsOnly || isSystemSymbol()) {
return *this;
}
// Check that this is not a parameter in a parametered expression
Expression ancestor = *this;
while (parameteredAncestorsCount > 0) {
ancestor = ancestor.parent();
assert(!ancestor.isUninitialized());
if (ancestor.isParameteredExpression()) {
parameteredAncestorsCount--;
Symbol ancestorParameter = static_cast<ParameteredExpression&>(ancestor).parameter();
if (hasSameNameAs(ancestorParameter)) {
return *this;
}
}
}
Expression e = context->expressionForSymbolAbstract(*this, true);
if (e.isUninitialized()) {
return *this;